Shiro 基于权限授权的简单应用与授权注解

太过爱你忘了你带给我的痛 2022-02-15 13:07 502阅读 0赞

上篇文章有必要看一下:Shiro 基于角色授权的简单应用与权限过滤器配置含义

一、基于权限授权的简单应用

数据库:

  1. ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQyNDAyODU0_size_16_color_FFFFFF_t_70][]
  2. 用户admin, 只拥有角色 admin user 与访问 /admin/userlist 的权限资源,如果 pid 12,则两者都可访问。

1、spring.xml 在配置 shiro 的过滤器上,配置权限控制的拦截规则:

注意:规则是有顺序的,从上到下,拦截范围必须是从小到大的

  1. <!-- 配置shiro的一些拦截规则,id必须和web.xml中的 shiro 拦截器名一致 -->
  2. <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
  3. <!-- Shiro的核心安全接口,这个属性是必须的 -->
  4. <property name="securityManager" ref="securityManager" />
  5. <!-- 身份认证失败,则跳转到登录页面的配置 -->
  6. <property name="loginUrl" value="/login" />
  7. <!-- 登录成功后的页面 -->
  8. <property name="successUrl" value="/admin/index" />
  9. <!-- 权限认证失败,则跳转到指定页面 -->
  10. <property name="unauthorizedUrl" value="/unauthorized" /> <!-- 登录后访问没有权限的页面后跳转的页面 -->
  11. <!-- Shiro连接约束配置,即过滤链的定义 -->
  12. <property name="filterChainDefinitions">
  13. <value>
  14. <!-- 注意:规则是有顺序的,从上到下,拦截范围必须是从小到大的
  15. url = 拦截规则(anon为匿名,authc为要登录后,才能访问,logout登出过滤) -->
  16. /login = anon
  17. /logout = logout
  18. /admin/userlist = perms[userlist]
  19. /admin/addUser = perms[addUser]
  20. /admin/** = authc
  21. /**= anon
  22. </value>
  23. </property>
  24. </bean>

2、自定义 ShiroRealm类的继承 AuthorizingRealm 类:

  1. 登录认证方法不用修改,授权认证方法做相应修改
  2. /**
  3. * 在 shiro 中专门做授权认证的方法
  4. */
  5. @Override
  6. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
  7. //1 从参数 principals 中获取当前登录成功后的用户信息
  8. User user = principals.oneByType(User.class);
  9. //2 根据第一步中的用户信息,获取角色信息(若用户信息包含角色/权限信息,直接取出,若没有,从数据库中获取)
  10. Set<String> roles = RoleMapper.getRolesByUserid(user.getId());
  11. // 通过用户关联的role信息,获取角色关联的 permisssion信息
  12. Set<String> permissions = permissionMapper.getPermissionsByUserid(user.getId());
  13. //3 把获取到的登录用户关联的角色和权限资源信息注入到返回的SimpleAuthorizationInfo对象中
  14. SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
  15. info.addRoles(roles);
  16. info.addStringPermissions(permissions);
  17. return info;
  18. }

PermissionMapper 方法:

  1. <select id="getPermissionsByUserid" resultType="String">
  2. select
  3. p.pname
  4. from
  5. t_user u,t_role r,t_user_role ur,t_permission p,t_role_permission rp
  6. where
  7. u.id=ur.userid and ur.roleid=r.id and r.id=rp.rid and rp.pid=p.id
  8. and u.id = #{userid}
  9. </select>

3、登录访问项目

  1. 结果和数据库分析一致

二、Shiro 的授权注解

1、5个授权注解

  1. @RequiresRoles(value = \{"admin","user"\},logical = Logical.AND)
  2. 当前Subject必须拥有所有指定的角色时,**才能访问被该注解标注的方法**。如果当Subject不同时拥有所有指定角色,则方法**不会执行还会抛出AuthorizationException异常**。
  3. @RequiresPermissions(value = \{"userlist","userlist2"\},logical=Logical.OR)
  4. 当前Subject需要拥有某些特定的权限时,**才能执行被该注解标注的方法**。如果当前Subject不具有这样的权限,则**方法不会被执行**。
  5. 认证通过后接受 Shiro 授权检查,授权验证时,**需要先判断当前角色是否拥有该权限**,只有授权通过,才可以访问受保护 URL 对应的资源,否则跳转到“未经授权页面”。
  6. @RequiresAuthentication
  7. 用于表明当前用户需是经过认证的用户。使用该注解标注的类/实例/方法在访问或调用时,要求**当前Subject 必须在当前的session 中被认证通过才能被访问或调用。即 Subjec.isAuthenticated()返回 true**。
  8. @RequiresUser
  9. 该注解需要**当前的Subject 是一个应用程序用户**才能被注解的类/实例/方法访问或调用。一个“**应用程序用户**”被定义为一个拥有已知身份,或在当前session 中由于通过验证被确认,或者在之前session 中的'RememberMe'服务被记住。
  10. @RequiresGuest
  11. 使用该注解标注的类/实例/方法在访问或调用时,当前Subject可以是“gust”身份,不需要经过认证或者在原先的session中存在记录,**即游客身份**。
  12. ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQyNDAyODU0_size_16_color_FFFFFF_t_70 1][]

2、使用方法

  1. Shiro 的授权注解处理是有内定的处理顺序的,如果有多个注解的话,前面的通过了会继续检查后面的,若不通过则直接返回,**处理顺序依次为(与实际声明顺序无关)**:
  2. RequiresRoles
  3. RequiresPermissions
  4. RequiresAuthentication
  5. RequiresUser
  6. RequiresGuest

例如:你如果同时声明了 RequiresRoles 和 RequiresPermissions 注解,那就要求拥有此角色的同时还得拥有相应的权限。

三、基于权限授权的简单注解应用

使用注解 实现 第一点 的功能

1、spring.xml 在配置 shiro 的过滤器上

取消配置权限控制的拦截规则,

添加一个SimpleMappingExceptionResolver异常处理。

  1. <!-- 配置shiro的一些拦截规则,id必须和web.xml中的 shiro 拦截器名一致 -->
  2. <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
  3. <!-- Shiro的核心安全接口,这个属性是必须的 -->
  4. <property name="securityManager" ref="securityManager" />
  5. <!-- 身份认证失败,则跳转到登录页面的配置 -->
  6. <property name="loginUrl" value="/login" />
  7. <!-- 登录成功后的页面 -->
  8. <property name="successUrl" value="/admin/index" />
  9. <!-- 权限认证失败,则跳转到指定页面 -->
  10. <property name="unauthorizedUrl" value="/unauthorized" /> <!-- 登录后访问没有权限的页面后跳转的页面 -->
  11. </bean>
  12. <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
  13. <property name="exceptionMappings">
  14. <props>
  15. <prop key="org.apache.shiro.authz.UnauthorizedException">unauthorized</prop>
  16. <prop key="org.apache.shiro.authz.UnauthenticatedException">login</prop>
  17. </props>
  18. </property>
  19. </bean>

2、自定义 ShiroRealm类的继承 AuthorizingRealm 类:和第一点相同处理

3、action 类中进行注解授权:

  1. @RequiresGuest
  2. @GetMapping(value= {"/","/login"})
  3. public String login(Model model, HttpSession session) {
  4. //生成一组16位随机数
  5. int hashCodeValue = UUID.randomUUID().hashCode();
  6. if(hashCodeValue < 0) hashCodeValue = -hashCodeValue;
  7. String uuidSalt = String.format("%016d",hashCodeValue);//左边补0,16位,进制(d,x)
  8. //把uuid盐值,同时保存在前后端
  9. model.addAttribute("uuidSalt", uuidSalt);
  10. session.setAttribute("uuidSalt", uuidSalt);
  11. return "login";
  12. }
  13. @RequiresGuest
  14. @PostMapping("/login")
  15. public String login(User user, HttpSession session) {
  16. //使用 shiro 登录验证
  17. //1 认证的核心组件:获取 Subject 对象
  18. Subject subject = SecurityUtils.getSubject();
  19. //2 将登陆表单封装成 token 对象
  20. UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPazzword());
  21. try {
  22. //3 让 shiro 框架进行登录验证:
  23. subject.login(token);
  24. } catch (Exception e) {
  25. e.printStackTrace();
  26. return "loginError";
  27. }
  28. return "redirect:/admin/index";
  29. }
  30. @RequiresAuthentication
  31. @GetMapping("/admin/index")
  32. public String admin(Model model) {
  33. return "admin/index";
  34. }
  35. @RequiresPermissions(value = {"userlist","userlist2"},logical=Logical.OR)
  36. @GetMapping("/admin/userlist")
  37. public String userlist() {
  38. return "admin/userlist";
  39. }
  40. @RequiresPermissions(value = {"addUser"})
  41. @GetMapping("/admin/addUser")
  42. public String addUser() {
  43. return "admin/addUser";
  44. }
  45. @GetMapping("/unauthorized")
  46. public String unauthorized() {
  47. return "unauthorized";
  48. }
  49. /**
  50. * shrio 使用注解之后,需要自己实现退出
  51. */
  52. @GetMapping("/logout")
  53. public String logout() {
  54. SecurityUtils.getSubject().logout(); //实现退出
  55. return "redirect:/login";
  56. }

4、登录访问项目:

结果和上面一致

注意:使用注解后的2个问题

1、退出登录需要自己实现(SecurityUtils.getSubject().logout();)。

2、没有登录认证访问无访问权限的页面后的跳转要自己配置实现(添加一个SimpleMappingExceptionResolver异常处理)。

不处理这两个问题会出现这个:

2019042511021035.png

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQyNDAyODU0_size_16_color_FFFFFF_t_70 2

5、测试一下同时声明了 RequiresRoles 和 RequiresPermissions 注解

  1. @RequiresRoles(value = {"admin2","user"},logical=Logical.AND)
  2. @RequiresPermissions(value = {"userlist","userlist2"},logical=Logical.OR)
  3. @GetMapping("/admin/userlist")
  4. public String userlist() {
  5. System.out.println("admin");
  6. return "admin/userlist";
  7. }
  8. 用户 admin,拥有 admin user 角色,userlist 权限资源,所以上面会跳转到 无访问权限 页面。
  9. **RequiresRoles** admin2 改为 admin 时, 认证通过,能够访问 /admin/userlist 权限资源。

end ~

发表评论

表情:
评论列表 (有 0 条评论,502人围观)

还没有评论,来说两句吧...

相关阅读