Shiro — это мощная и простая в использовании среда безопасности Java, в основном используемая для более удобной аутентификации, авторизации, шифрования и управления сеансами. Первая и главная цель Широ — сделать его простым в использовании и понятным.
Особенности Широ
Shiro — это комплексная система безопасности с множеством функций, на следующем рисунке можно понять особенности Shiro:
- Аутентификация: Аутентификация/вход в систему, проверка наличия у пользователя соответствующего удостоверения.
- Авторизация: авторизация, то есть проверка разрешений, проверка наличия у аутентифицированного пользователя определенных разрешений, то есть оценка того, может ли пользователь делать что-либо, например: проверка наличия у пользователя определенной роли. Или детальная проверка наличия у пользователя определенного разрешения на определенный ресурс.
- Диспетчер сеансов: Управление сеансом, то есть после входа пользователя в систему это сеанс, а перед выходом из системы вся его информация находится в сеансе; сеанс может быть в обычной среде JavaSE или в веб-среде.
- Криптография: шифрование, которое защищает безопасность данных, таких как пароли, зашифрованы и хранятся в базе данных вместо открытого текста.
- Веб-поддержка: Веб-поддержка может быть легко интегрирована в веб-среду.
- Кэширование: Кэширование.Например, после входа пользователя в систему его информацию о пользователе и роли/разрешения не нужно проверять каждый раз, что может повысить эффективность.
- Параллелизм: Широ поддерживает параллельную проверку многопоточных приложений, то есть, если один поток запускает другой поток, разрешения могут распространяться автоматически.
- Тестирование: обеспечивает поддержку тестирования.
- Запуск от имени: разрешает доступ одному пользователю, выдающему себя за другого пользователя (если он это разрешает).
- Запомнить меня: Запомнить меня, это очень распространенная функция, то есть авторизовавшись один раз, вам не нужно авторизоваться в следующий раз, когда вы придете. 2.2 Принцип работы
- Тема: Тема, представляющая текущего «пользователя». Этот пользователь не обязательно является конкретным человеком, все, что взаимодействует с текущим приложением, является субъектом, например, веб-краулер, робот и т. д. Все субъекты привязаны к SecurityManager, и все взаимодействия с субъектом делегируются SecurityManager. Мы можем думать о Subject как о фасаде, а SecurityManager — как о фактическом исполнителе.
- SecurityManager: Менеджер безопасности. То есть все операции, связанные с безопасностью, взаимодействуют с SecurityManager, который управляет всеми субъектами. Видно, что это ядро Широ, которое отвечает за взаимодействие с другими компонентами, представленными позже.Если вы изучили SpringMVC, мы можем рассматривать его как фронт-контроллер DispatcherServlet.
- Царство: Царство. Shiro получает данные безопасности (такие как пользователи, роли, разрешения) из Realm, а это означает, что если SecurityManager хочет проверить личность пользователя, ему необходимо получить соответствующего пользователя из Realm для сравнения, чтобы определить, является ли личность пользователя законной, а также необходимо получить соответствующую роль пользователя от Realm. /Permission, чтобы проверить, может ли пользователь работать. Мы можем думать о Realm как об источнике данных, безопасном источнике данных.
Принципиальная схема работы и внутренняя архитектура Shiro выглядят следующим образом:
- Субъект: Субъект, как видите, субъектом может быть любой «пользователь», взаимодействующий с приложением.
- SecurityManager: эквивалент DispatcherServlet в SpringMVC или FilterDispatcher в Struts2. Это ядро Широ, и все конкретные взаимодействия контролируются через SecurityManager. Он управляет всеми Субъектами и отвечает за аутентификацию и авторизацию, а также за управление сессиями и кешем.
- Аутентификатор: Аутентификатор, отвечающий за аутентификацию субъекта, это точка расширения, если пользователи считают, что Широ по умолчанию не подходит, мы можем настроить реализацию. Для этого требуется стратегия аутентификации, то есть, при каких обстоятельствах проходит аутентификация пользователя.
- Авторизатор: авторизатор или контроллер доступа. Он используется для определения того, имеет ли субъект разрешение на выполнение соответствующей операции, то есть контролирует, к каким функциям в приложении может получить доступ пользователь.
- Область: может быть одна или несколько областей, которые можно рассматривать как источник данных объекта безопасности, то есть используемый для получения объекта безопасности. Это может быть реализация JDBC, реализация LDAP, реализация в памяти и т. д.
- SessionManager: если вы написали сервлет, вы должны знать концепцию Session. Session нужен кто-то, кто будет управлять его жизненным циклом. Этот компонент SessionManager. И Широ можно использовать не только в веб-среде, но и в обычной среде JavaSE.
- SessionDAO: DAO используется всеми, объекты доступа к данным, CRUD для сессий. Мы можем настроить реализацию SessionDAO, чтобы контролировать, где хранится сеанс. Например, запись в базу данных через JDBC или запись в Redis через jedis. Кроме того, Cache можно использовать в SessionDAO для кэширования с целью повышения производительности.
- CacheManager: Менеджер кеша. Он управляет кэшами, такими как пользователи, роли, разрешения и т. д. Поскольку эти данные в основном редко изменяются, помещение их в кэш может повысить производительность доступа.
- Криптография: модуль криптографии Широ улучшает некоторые общие криптографические компоненты, например, криптографическое шифрование/дешифрование.
фильтр
| аббревиатура фильтра | Соответствующий класс Java |
|---|---|
| anon | org.apache.shiro.web.filter.authc.AnonymousFilter |
| authc | org.apache.shiro.web.filter.authc.FormAuthenticationFilter |
| authcBasic | org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter |
| perms | org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter |
| port | org.apache.shiro.web.filter.authz.PortFilter |
| rest | org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter |
| roles | org.apache.shiro.web.filter.authz.RolesAuthorizationFilter |
ssl |org.apache.shiro.web.filter.authz.SslFilter
user |org.apache.shiro.web.filter.authc.UserFilter
logout |org.apache.shiro.web.filter.authc.LogoutFilter noSessionCreation |org.apache.shiro.web.filter.session.NoSessionCreationFilter
/admins/**=auth # 表示该 uri 需要认证才能访问
/admins/**=authcBasic # 表示该 uri 需要 httpBasic 认证
/admins/**=perms[user:add:*] # 表示该 uri 需要认证用户拥有 user:add:* 权限才能访问
/admins/**=port[8081] # 表示该 uri 需要使用 8081 端口
/admins/**=rest[user] # 相当于 /admins/**=perms[user:method],其中,method 表示 get、post、delete 等
/admins/**=roles[admin] # 表示该 uri 需要认证用户拥有 admin 角色才能访问
/admins/**=ssl # 表示该 uri 需要使用 https 协议
/admins/**=user # 表示该 uri 需要认证或通过记住我认证才能访问
/logout=logout # 表示注销,可以当作固定配置
注意:
anon,authcBasic,auchc,user 是认证过滤器。
perms,roles,ssl,rest,port 是授权过滤器。
Создать проект Широ Добавьте следующие зависимости в pom.xml:
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.6.1</version>
</dependency>
</dependencies>
конфигурационный файл
Создайте файл конфигурации с именем shiro.ini в каталоге src/main/resources со следующим содержимым.
[users]
# admin=admin 分别表示账号和密码,administrator 表示逗号前边的账号拥有 administrator 这个角色。
admin=admin,administrator
zhangsan=zhangsan,manager
lisi=lisi,guest
[roles]
# administrator 表示角色名称,* 表示这个角色拥有所有权限
administrator=*
manager=user:*,department:*
guest=user:query,department:query
тестовый класс
public class ShiroTest {
@Test
public void test() {
// 读取 shiro.ini 文件内容
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject currentUser = SecurityUtils.getSubject();
Session session = currentUser.getSession();
session.setAttribute("someKey", "aValue");
String value = (String) session.getAttribute("someKey");
if (value.equals("aValue")) {
System.out.println("someKey 的值:" + value);
}
// 登陆
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "zhangsan");
token.setRememberMe(true);
try {
currentUser.login(token);
} catch (UnknownAccountException uae) {
System.out.println("用户名不存在:" + token.getPrincipal());
} catch (IncorrectCredentialsException ice) {
System.out.println("账户密码 " + token.getPrincipal() + " 不正确!");
} catch (LockedAccountException lae) {
System.out.println("用户名 " + token.getPrincipal() + " 被锁定 !");
}
// 认证成功后
if (currentUser.isAuthenticated()) {
System.out.println("用户 " + currentUser.getPrincipal() + " 登陆成功!");
//测试角色
System.out.println("是否拥有 manager 角色:" + currentUser.hasRole("manager"));
//测试权限
System.out.println("是否拥有 user:create 权限" + currentUser.isPermitted("user:create"));
//退出
currentUser.logout();
}
}
}
Используйте аннотации для проверки авторизации пользователя
Прежде чем использовать аннотации Широ, убедитесь, что в проект добавлены соответствующие пакеты jar, поддерживающие функции АОП. Общие аннотации следующие:
@RequiresRoles( "manager" ) # 角色校验
public String save() {
//TODO
}
@RequiresPermissions("user:manage") # 权限检验
public String delete() {
//TODO
}
Пользовательский мир
Вышеупомянутая программа использует IniRealm, поставляемый с Shiro, и IniRealm считывает информацию о пользователе из файла конфигурации ini.В большинстве случаев информацию о пользователе необходимо считывать из системной базы данных, поэтому вам необходимо настроить область.
public class CustomRealm extends AuthorizingRealm {
/**
* 认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 从 token 中获取用户身份信息
String username = (String) token.getPrincipal();
// 通过 username 从数据库中查询
// 如果查询不到则返回 null
if(!username.equals("zhangsan")){//这里模拟查询不到
return null;
}
//获取从数据库查询出来的用户密码
String dbPassword = "zhangsan";//这里使用静态数据模拟
//返回认证信息由父类 AuthenticatingRealm 进行认证
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, dbPassword, getName());
return simpleAuthenticationInfo;
}
/**
* 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 获取身份信息
String username = (String) principals.getPrimaryPrincipal();
// 根据身份信息从数据库中查询权限数据
// 这里使用静态数据模拟
List<String> permissions = new ArrayList<String>();
permissions.add("user:*");
permissions.add("department:*");
// 将权限信息封闭为AuthorizationInfo
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
// 模拟数据,添加 manager 角色
simpleAuthorizationInfo.addRole("manager");
for(String permission:permissions){
simpleAuthorizationInfo.addStringPermission(permission);
}
return simpleAuthorizationInfo;
}
}
Создайте файл shiro-realm.ini в каталоге src/main/resources со следующим содержимым:
[main]
#自定义 realm
customRealm=com.light.shiroTest.realm.CustomRealm
#将realm设置到securityManager
securityManager.realms=$customRealm
Интеграция с Spring
добавить зависимости
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
конфигурационный файл
веб.xml:
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
приложение-shiro.xml:
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 必须设置 -->
<property name="securityManager" ref="securityManager"/>
<!-- 3 个 url 属性为可选设置 -->
<property name="loginUrl" value="/login.jsp"/>
<property name="successUrl" value="/home.jsp"/>
<property name="unauthorizedUrl" value="/unauthorized.jsp"/>
<property name="filterChainDefinitions">
<value>
<!-- 对静态资源设置匿名访问 -->
/resources/** = anon
/login = anon
<!-- /** = authc 所有url都必须认证通过才可以访问-->
/** = authc
</value>
</property>
</bean>
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="customRealm" />
</bean>
<!-- 自定义 realm -->
<bean id="customRealm" class="com.light.ac.web.realm.CustomRealm"></bean>
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
Среди них имя shiroFilter в application-shiro.xml соответствует имени shiroFilter в файле web.xml и должно быть непротиворечивым. anon и authc соответствуют упомянутым выше фильтрам.
Класс CustomRealm:
public class CustomRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Autowired
private PermissionService permissionService;
/**
* 认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 获取用户名
String userName = (String) token.getPrincipal();
// 通过用户名获取用户对象
User user = this.userService.findUserByUserName(userName);
if (user == null) {
return null;
}
// 通过 userId 获取该用户拥有的所有权限,返回值根据自己需求编写,并非固定值。
Map<String,List<Permission>> permissionMap = this.permissionService.getPermissionMapByUserId(user.getId());
// (目录+菜单,分层级,用于前端 jsp 遍历)
user.setMenuList(permissionMap.get("menuList"));
// (目录+菜单+按钮,用于后端权限判断)
user.setPermissionList(permissionMap.get("permissionList"));
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
return info;
}
/**
* 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
User user = (User) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// (目录+菜单+按钮,用于后端权限判断)
List<Permission> permissionList = user.getPermissionList();
for (Permission permission : permissionList) {
if (StringUtil.isNotEmpty(permission.getCode())) {
info.addStringPermission(permission.getCode());
}
}
return info;
}
}