предисловие
Основные моменты знаний, объясненные в этой статье, следующие:
- Краткое введение в способ авторизации Широ
- Интеграция с Spring
- Начальный фильтр Широ
A, Широ авторизация
В предыдущей статье мы объяснили знания, связанные с аутентификацией Широ, теперь давайте получим авторизацию Широ.
Процесс авторизации Shiro и процесс сертификации на самом деле похожи:
1.1 Методы авторизации, поддерживаемые Широ
Shiro поддерживает три метода авторизации:
Shiro 支持三种方式的授权:
编程式:通过写if/else 授权代码块完成:
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole(“admin”)) {
//有权限
} else {
//无权限
}
注解式:通过在执行的Java方法上放置相应的注解完成:
@RequiresRoles("admin")
public void hello() {
//有权限
}
JSP/GSP 标签:在JSP/GSP 页面通过相应的标签完成:
<shiro:hasRole name="admin">
<!— 有权限—>
</shiro:hasRole>
1.2 Использование программной авторизации
Аналогично авторизуемся через менеджера безопасности, поэтому еще нужно настроить соответствующий конфигурационный файл:
Файл конфигурации Shiro-Permission.ini:
#用户
[users]
#用户zhang的密码是123,此用户具有role1和role2两个角色
zhang=123,role1,role2
wang=123,role2
#权限
[roles]
#角色role1对资源user拥有create、update权限
role1=user:create,user:update
#角色role2对资源user拥有create、delete权限
role2=user:create,user:delete
#角色role3对资源user拥有create权限
role3=user:create
#权限标识符号规则:资源:操作:实例(中间使用半角:分隔)
user:create:01 表示对用户资源的01实例进行create操作。
user:create:表示对用户资源进行create操作,相当于user:create:*,对所有用户资源实例进行create操作。
user:*:01 表示对用户资源实例01进行所有操作。
Проверьте код:
// 角色授权、资源授权测试
@Test
public void testAuthorization() {
// 创建SecurityManager工厂
Factory<SecurityManager> factory = new IniSecurityManagerFactory(
"classpath:shiro-permission.ini");
// 创建SecurityManager
SecurityManager securityManager = factory.getInstance();
// 将SecurityManager设置到系统运行环境,和spring后将SecurityManager配置spring容器中,一般单例管理
SecurityUtils.setSecurityManager(securityManager);
// 创建subject
Subject subject = SecurityUtils.getSubject();
// 创建token令牌
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan",
"123");
// 执行认证
try {
subject.login(token);
} catch (AuthenticationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("认证状态:" + subject.isAuthenticated());
// 认证通过后执行授权
// 基于角色的授权
// hasRole传入角色标识
boolean ishasRole = subject.hasRole("role1");
System.out.println("单个角色判断" + ishasRole);
// hasAllRoles是否拥有多个角色
boolean hasAllRoles = subject.hasAllRoles(Arrays.asList("role1",
"role2", "role3"));
System.out.println("多个角色判断" + hasAllRoles);
// 使用check方法进行授权,如果授权不通过会抛出异常
// subject.checkRole("role13");
// 基于资源的授权
// isPermitted传入权限标识符
boolean isPermitted = subject.isPermitted("user:create:1");
System.out.println("单个权限判断" + isPermitted);
boolean isPermittedAll = subject.isPermittedAll("user:create:1",
"user:delete");
System.out.println("多个权限判断" + isPermittedAll);
// 使用check方法进行授权,如果授权不通过会抛出异常
subject.checkPermission("items:create:1");
}
1.3 Настройка области для авторизации
Как правило, наши разрешения запрашиваются из базы данных, а не в соответствии с нашим файлом конфигурации. Поэтому нам нужно настроить reaml и позволить reaml сравнивать разрешения, запрашиваемые из базы данных.
Файл конфигурации shiro-realm.ini: ввод пользовательской информации об области в менеджер безопасности.
[main]
#自定义 realm
customRealm=cn.itcast.shiro.realm.CustomRealm
#将realm设置到securityManager,相当 于spring中注入
securityManager.realms=$customRealm
В прошлый раз мы использовали пользовательский reaml, просто переопределив метод doGetAuthenticationInfo(),На этот раз мы переопределяем метод doGetAuthorizationInfo().
// 用于授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
//从 principals获取主身份信息
//将getPrimaryPrincipal方法返回值转为真实身份类型(在上边的doGetAuthenticationInfo认证通过填充到SimpleAuthenticationInfo中身份类型),
String userCode = (String) principals.getPrimaryPrincipal();
//根据身份信息获取权限信息
//连接数据库...
//模拟从数据库获取到数据
List<String> permissions = new ArrayList<String>();
permissions.add("user:create");//用户的创建
permissions.add("items:add");//商品添加权限
//....
//查到权限数据,返回授权信息(要包括 上边的permissions)
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//将上边查询到授权信息填充到simpleAuthorizationInfo对象中
simpleAuthorizationInfo.addStringPermissions(permissions);
return simpleAuthorizationInfo;
}
тестовая программа:
// 自定义realm进行资源授权测试
@Test
public void testAuthorizationCustomRealm() {
// 创建SecurityManager工厂
Factory<SecurityManager> factory = new IniSecurityManagerFactory(
"classpath:shiro-realm.ini");
// 创建SecurityManager
SecurityManager securityManager = factory.getInstance();
// 将SecurityManager设置到系统运行环境,和spring后将SecurityManager配置spring容器中,一般单例管理
SecurityUtils.setSecurityManager(securityManager);
// 创建subject
Subject subject = SecurityUtils.getSubject();
// 创建token令牌
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan",
"111111");
// 执行认证
try {
subject.login(token);
} catch (AuthenticationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("认证状态:" + subject.isAuthenticated());
// 认证通过后执行授权
// 基于资源的授权,调用isPermitted方法会调用CustomRealm从数据库查询正确权限数据
// isPermitted传入权限标识符,判断user:create:1是否在CustomRealm查询到权限数据之内
boolean isPermitted = subject.isPermitted("user:create:1");
System.out.println("单个权限判断" + isPermitted);
boolean isPermittedAll = subject.isPermittedAll("user:create:1",
"user:create");
System.out.println("多个权限判断" + isPermittedAll);
// 使用check方法进行授权,如果授权不通过会抛出异常
subject.checkPermission("items:add:1");
}
2. Интеграция Spring и Shiro
2.1 Импорт пакета jar
- баночка широ-паутины,
- баночка широ-спринг
- баночка широкода
2.2 Быстрый старт
Широ тоже перехватывается фильтром.Фильтр перехватывается после передачи операции в цепочку фильтров, настроенную в Spring.
Настроить фильтр в web.xml
<!-- shiro的filter -->
<!-- shiro过虑器,DelegatingFilterProxy通过代理模式将spring容器中的bean和filter关联起来 -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<!-- 设置true由servlet容器控制filter的生命周期 -->
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
<!-- 设置spring容器filter的bean id,如果不设置则找与filter-name一致的bean-->
<init-param>
<param-name>targetBeanName</param-name>
<param-value>shiroFilter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
существуетВ applicationContext-shiro.xml настройте фитлер в web.xml, соответствующий bean-компоненту в контейнере spring..
<!-- web.xml中shiro的filter对应的bean -->
<!-- Shiro 的Web过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 -->
<property name="loginUrl" value="/login.action" />
<!-- 认证成功统一跳转到first.action,建议不配置,shiro认证成功自动到上一个请求路径 -->
<!-- <property name="successUrl" value="/first.action"/> -->
<!-- 通过unauthorizedUrl指定没有权限操作时跳转页面-->
<property name="unauthorizedUrl" value="/refuse.jsp" />
<!-- 自定义filter配置 -->
<property name="filters">
<map>
<!-- 将自定义 的FormAuthenticationFilter注入shiroFilter中-->
<entry key="authc" value-ref="formAuthenticationFilter" />
</map>
</property>
<!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
<property name="filterChainDefinitions">
<value>
<!--所有url都可以匿名访问-->
/** = anon
</value>
</property>
</bean>
Настройка диспетчера безопасности
<!-- securityManager安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="customRealm" />
</bean>
настроить область
<!-- realm -->
<bean id="customRealm" class="cn.itcast.ssm.shiro.CustomRealm">
</bean>
шаг:
- Фильтры Широ в файле web.xml
- Настройте соответствующую filterChain в соответствующем файле конфигурации Spring (цепочка фильтров).
- Настройте диспетчер безопасности и внедрите пользовательскую область
- Настроить пользовательскую область
2.3 Статические ресурсы не перехватываются
Когда мы весной настроили цепочку фильтров, мы нашли эту строку кода:
<!--所有url都可以匿名访问 -->
/** = anon
анон на самом деле фильтр, встроенный в широ, приведенный выше код означает, что все анонимные пользователи могут получить доступ
Конечно, нам также нужно настроить другую информацию позже,Для того, чтобы страница нормально отображалась, наши статические ресурсы вообще не нужно перехватывать.
Таким образом, мы можем настроить его следующим образом:
<!-- 对静态资源设置匿名访问 -->
/images/** = anon
/js/** = anon
/styles/** = anon
3. Первое знакомство с фильтром широ
Выше мы узнали про anno filter, shiro и другие фильтры. Давайте посмотрим
Обычно используются следующие фильтры:
анон: пример/admins/**=anon
Параметров нет, а значит можно использовать анонимно.
авторизация: например/admins/user/**
=authc означает, что для использования требуется аутентификация (логин), FormAuthenticationFilter — аутентификация формы, без параметров
завивка: пример/admins/user/**=perms[user:add:*]
, Можно записать несколько параметров, и при использовании нескольких параметров необходимо добавить кавычки, а параметры разделяются запятыми, например/admins/user/**=perms["user:add:*,user:modify:*"]
, при наличии нескольких параметров каждый параметр должен быть передан перед передачей, что эквивалентно методу isPermitedAll().
пользователь: например/admins/user/**
= Пользователь не указан, он указывает, что пользователь должен существовать, аутентификация может быть доступна или сертифицирована, записав меня, и когда операция не подписывает проверки
3.1 Вход и выход
Реализуется с помощью фильтра FormAuthenticationFilter, принцип следующий:
- Когда пользователь не аутентифицирован,Запросить loginurl для аутентификации [мы настроили выше], идентификатор пользователя и пароль пользователя отправить данные на loginurl
- FormAuthenticationFilter перехватывает логин и пароль в запросе (Два имени параметра настраиваются)
- FormAuthenticationFilter Вызов области для передачи токена(имя пользователя и пароль)
- Запрос информации о пользователе на основе имени пользователя во время проверки подлинности области(хранится в Activeuser, включая идентификатор пользователя, код пользователя, имя пользователя, меню).
- Если запрос не может быть найден, область возвращает значение null, а FormAuthenticationFilter заполняет параметр в поле запроса (записывает информацию об исключении).
- После запроса информации о пользователе FormAuthenticationFilter автоматически сравнит информацию, возвращаемую reaml, с именем пользователя и паролем в токене. Если нет, верните исключение.
3.1.1 целевая страница
Из-за идентификатора пользователя и пароля FormAuthenticationFilterЗначение ввода по умолчанию (имя пользователя и пароль),Измените входное имя учетной записи и пароль страницы на имя пользователя и пароль
<TR>
<TD>用户名:</TD>
<TD colSpan="2"><input type="text" id="usercode"
name="username" style="WIDTH: 130px" /></TD>
</TR>
<TR>
<TD>密 码:</TD>
<TD><input type="password" id="pwd" name="password" style="WIDTH: 130px" />
</TD>
</TR>
3.1.2 Реализация кода входа
Как мы уже говорили выше, когда пользователь не аутентифицирован,Запрошенный логин-url аутентифицируется, и пользовательский пароль удостоверения пользователя отправляет данные в логин-рул..
Когда мы отправляемся на loginurl,Фильтр формы автоматически анализирует имя пользователя и пароль, чтобы вызвать область для аутентификации. Наконец, информация аутентификации shiroLoginFailure хранится в объекте домена запроса.Если возвращаемая информация является ненормальной, мы можем выдать исключение при входе в систему.
//登陆提交地址,和applicationContext-shiro.xml中配置的loginurl一致
@RequestMapping("login")
public String login(HttpServletRequest request)throws Exception{
//如果登陆失败从request中获取认证异常信息,shiroLoginFailure就是shiro异常类的全限定名
String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");
//根据shiro返回的异常类路径判断,抛出指定异常信息
if(exceptionClassName!=null){
if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
//最终会抛给异常处理器
throw new CustomException("账号不存在");
} else if (IncorrectCredentialsException.class.getName().equals(
exceptionClassName)) {
throw new CustomException("用户名/密码错误");
} else if("randomCodeError".equals(exceptionClassName)){
throw new CustomException("验证码错误 ");
}else {
throw new Exception();//最终在异常处理器生成未知错误
}
}
//此方法不处理登陆成功(认证成功),shiro认证成功会自动跳转到上一个请求路径
//登陆失败还到login页面
return "login";
}
Настройка фильтров аутентификации
<value>
<!-- 对静态资源设置匿名访问 -->
/images/** = anon
/js/** = anon
/styles/** = anon
<!-- /** = authc 所有url都必须认证通过才可以访问-->
/** = authc
</value>
3.2 Выход
Не надо нам выход осуществлять,Просто посетите URL-адрес вывода (URL-адрес отсутствует), LogoutFilter для перехвата, очистки сеанса.
Настройте LogoutFilter в applicationContext-shiro.xml:
<!-- 请求 logout.action地址,shiro去清除session-->
/logout.action = logout
4. После аутентификации информация будет отображаться на странице
1. После аутентификации на главной странице отобразится меню пользователя. 2. После аутентификации информация о пользователе отображается в шапке страницы
realm запрашивает информацию о пользователе из базы данных,Установите меню пользователя, код пользователя, имя пользователя и т. д. в SimpleAuthenticationInfo.
//realm的认证方法,从数据库查询用户信息
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
// token是用户输入的用户名和密码
// 第一步从token中取出用户名
String userCode = (String) token.getPrincipal();
// 第二步:根据用户输入的userCode从数据库查询
SysUser sysUser = null;
try {
sysUser = sysService.findSysUserByUserCode(userCode);
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// 如果查询不到返回null
if(sysUser==null){//
return null;
}
// 从数据库查询到密码
String password = sysUser.getPassword();
//盐
String salt = sysUser.getSalt();
// 如果查询到返回认证信息AuthenticationInfo
//activeUser就是用户身份信息
ActiveUser activeUser = new ActiveUser();
activeUser.setUserid(sysUser.getId());
activeUser.setUsercode(sysUser.getUsercode());
activeUser.setUsername(sysUser.getUsername());
//..
//根据用户id取出菜单
List<SysPermission> menus = null;
try {
//通过service取出菜单
menus = sysService.findMenuListByUserId(sysUser.getId());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//将用户菜单 设置到activeUser
activeUser.setMenus(menus);
//将activeUser设置simpleAuthenticationInfo
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
activeUser, password,ByteSource.Util.bytes(salt), this.getName());
return simpleAuthenticationInfo;
}
Настраиваем токенизатор, так как мы используем md5 и хеширование
<!-- 凭证匹配器 -->
<bean id="credentialsMatcher"
class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="md5" />
<property name="hashIterations" value="1" />
</bean>
<!-- realm -->
<bean id="customRealm" class="cn.itcast.ssm.shiro.CustomRealm">
<!-- 将凭证匹配器设置到realm中,realm按照凭证匹配器的要求进行散列 -->
<property name="credentialsMatcher" ref="credentialsMatcher"/>
</bean>
При переходе на домашнюю страницу удалите информацию об аутентификации пользователя, перешлите на JSP
//系统首页
@RequestMapping("/first")
public String first(Model model)throws Exception{
//从shiro的session中取activeUser
Subject subject = SecurityUtils.getSubject();
//取身份信息
ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
//通过model传到页面
model.addAttribute("activeUser", activeUser);
return "/first";
}
V. Резюме
- Существует три способа получения разрешений пользователя Shiro.
- программно
- Аннотированный
- Вкладки
- По умолчанию reaml Широ ищет информацию в конфигурационном файле для авторизации.Обычно нам нужен reaml для обращения к базе данных для запроса соответствующей информации. Поэтому вам нужно снова настроить реал
- В целом процесс аутентификации и авторизации аналогичен.
-
Spring интегрирован с Shiro, и фактические операции Shiro выполняются с помощью фильтров. Широ предоставляет нам множество фильтров.
- Настройте фильтр Широ в web.xml
- Используйте фильтры, настроенные в web.xml в файле конфигурации Shiro.
- Настройте класс диспетчера безопасности, настройте пользовательскую область и внедрите область в класс диспетчера безопасности. Оставьте менеджера по безопасности на фабрику Широ для управления.
- Установите статические ресурсы в цепочке фильтров, чтобы они не перехватывались.
- Использование фильтров в Shiro для аутентификации пользователя процесс заключается в следующем:
- Настройте путь запроса для аутентификации
- При доступе к пути запроса программиста Широ будет использовать FormAuthenticationFilter для вызова reaml для получения информации о пользователе.
- reaml может получить токен, получить информацию о пользователе из базы данных через имя пользователя и вернуть значение null, если пользователь не существует.
- FormAuthenticationFilter сравнивает данные, возвращаемые reaml, и выдает исключение, если они отличаются.
- Наш путь запроса используется только для определения того, выброшено ли исключение, а не для проверки.
- Shiro также предоставляет перехватчик для выходящих пользователей, нам просто нужно настроить URL-адрес.
- Когда нам нужно получить данные пользователя для эха, мы можем получить тему в SecurityUtils.getSubject(), а затем получить идентификационную информацию через тему.
Если в статье есть какие-либо ошибки, пожалуйста, поправьте меня, и мы сможем общаться друг с другом. Учащиеся, привыкшие читать технические статьи в WeChat и желающие получить больше ресурсов по Java, могутОбратите внимание на публичный аккаунт WeChat: Java3y