这篇文章给大家介绍Springboot整合Shiro中怎样进行权限管理,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。
Shiro的授权流程跟认证流程类似:
创建SecurityManager
安全管理器
Subject
主体带授权信息执行授权,请求到SecurityManager
SecurityManager
安全管理器调用Authorizer
授权
Authorizer
结合主体一步步传过来的授权信息与Realm
中的数据比对,授权
(1)我们已经在ShiroConfig配置类中配置好了SecurityManager
安全管理器
/** * 配置Shiro核心 安全管理器 SecurityManager * SecurityManager安全管理器:所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;负责与后边介绍的其他组件进行交互。(类似于SpringMVC中的DispatcherServlet控制器) */ @Bean public SecurityManager securityManager(){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //将自定义的realm交给SecurityManager管理 securityManager.setRealm(new CustomRealm()); return securityManager; }
(2)下面对自定义Realm类CustomRealm
中的授权方法doGetAuthorizationInfo
进行重写
@Override /** * 授权 */ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) { //获取当前登录的用户 User user = (User) principal.getPrimaryPrincipal(); //通过SimpleAuthenticationInfo做授权 SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); //添加角色 simpleAuthorizationInfo.addRole(user.getRole()); //添加权限 simpleAuthorizationInfo.addStringPermissions(user.getPermissions()); return simpleAuthorizationInfo; }
上面主要通过SimpleAuthorizationInfo
中的addRole
和addStringPermissions
添加当前用户拥有的角色和权限,与主体的授权信息进行比对。
(3)主体调用授权请求
主体进行授权请求有两种方式,一种是编程式,一种是注解式。
①编程式:通过Subject
的hasRole()
进行角色的校检,通过isPermitted()
进行权限的校检
@GetMapping("/dog") public String dog(){ Subject subject = SecurityUtils.getSubject(); if(subject.hasRole("dog")){ return "dog√"; } else { return "dog×"; } } @GetMapping("/cat") public String cat(){ Subject subject = SecurityUtils.getSubject(); if(subject.hasRole("cat")){ return "cat√"; } else { return "cat×"; } } @GetMapping("/rap") public String rap(){ Subject subject = SecurityUtils.getSubject(); if(subject.isPermitted("rap")){ return "rap"; }else{ return "没权限你Rap个锤子啊!"; }
模拟用户数据如下:
/** * 模拟数据库数据 * @return */ private List<User> getUsers(){ List<User> users = new ArrayList<>(2); List<String> cat = new ArrayList<>(3); cat.add("sing"); cat.add("rap"); List<String> dog = new ArrayList<>(3); dog.add("jump"); dog.add("basketball"); users.add(new User("张小黑的猫","123qwe",true,"cat",cat)); users.add(new User("张小黑的狗","123qwe",true,"dog",dog)); return users; }
测试结果如图:
authorization.gif
②注解式:
首先需要开启Aop注解,在ShiroConfig类中新增如下方法:
/** * 开启aop注解支持 * 即在controller中使用 @RequiresPermissions("user/userList") */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){ AuthorizationAttributeSourceAdvisor attributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); //设置安全管理器 attributeSourceAdvisor.setSecurityManager(securityManager); return attributeSourceAdvisor; } @Bean @ConditionalOnMissingBean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator(); defaultAAP.setProxyTargetClass(true); return defaultAAP; }
对于未授权的用户需要进行友好的提示,一般返回特定的403页面,在ShiroConfig类中新增如下方法实现:
/** * 处理未授权的异常,返回自定义的错误页面(403) * @return */ @Bean public SimpleMappingExceptionResolver simpleMappingExceptionResolver() { SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver(); Properties properties = new Properties(); /*未授权处理页*/ properties.setProperty("UnauthorizedException", "403.html"); resolver.setExceptionMappings(properties); return resolver; }
解释下上面的代码properties.setProperty("UnauthorizedException","403.html");
用来对抓取到UnauthorizedException
未授权异常进行处理,重定向到403.html
页面。我们需要创建403页面403.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>未授权</title> </head> <body> 403,您没有访问权限 </body> </html>
然后在Controller使用注解@RequiresRoles("xxx")
和@RequiresPermissions("xxx")
进行角色和权限的校检
@GetMapping("/sing") @RequiresRoles("cat") public String sing(){ return "sing"; } @GetMapping("/jump") @RequiresPermissions("jump") public String jump(){ return "jump"; }
测试结果如图:
authorization2.gif
总结:一般对未授权用户需要统一返回指定403页面的,使用注解更加方便;需要做业务逻辑(比如对未授权的请求记录进行预警等),使用编程式更加方便。
(4)前端的shiro标签
通常前端页面展示需要与用户的权限对等,即只给用户看到他们权限内的内容。(比如用户"张小黑的猫",对应角色“cat”,对应权限“sing”和“rap”,那么该用户登录后只展示Cat、sing和rap的按钮)
通常解决方式有两种:
其一:登录后通过读取数据库中角色和权限,获取需要展示的菜单内容,动态的在前端渲染;
其二:所有内容都在前端写好,通过前端的shiro标签控制对应权限内容部分的渲染。
这里给大家演示shiro标签的使用。(前端的shiro标签有Jsp标签、Freemarker标签、Thymeleaf标签等,演示用的为thymeleaf标签)
Shiro标签说明: guest标签:`<shiro:guest></shiro:guest>`,用户没有身份验证时显示相应信息,即游客访问信息。 user标签:`<shiro:user></shiro:user>`,用户已经身份验证/记住我登录后显示相应的信息。 authenticated标签:`<shiro:authenticated></shiro:authenticated>`,用户已经身份验证通过,即Subject.login登录成功,不是记住我登录的。 notAuthenticated标签:`<shiro:notAuthenticated></shiro:notAuthenticated>`,用户已经身份验证通过,即没有调用Subject.login进行登录,包括记住我自动登录的也属于未进行身份验证。 principal标签:`<shiro: principal/><shiro:principal property="username"/>`,相当`((User)Subject.getPrincipals()).getUsername()`。 lacksPermission标签:`<shiro:lacksPermission name="org:create"></shiro:lacksPermission>`,如果当前Subject没有权限将显示body体内容。 hasRole标签:`<shiro:hasRole name="admin"></shiro:hasRole>`,如果当前Subject有角色将显示body体内容。 hasAnyRoles标签:`<shiro:hasAnyRoles name="admin,user"></shiro:hasAnyRoles>`,如果当前Subject有任意一个角色(或的关系)将显示body体内容。 lacksRole标签:`<shiro:lacksRole name="abc"></shiro:lacksRole>`,如果当前Subject没有角色将显示body体内容。 hasPermission标签:`<shiro:hasPermission name="user:create"></shiro:hasPermission>`,如果当前Subject有权限将显示body体内容
使用:
①pom.xml引入相应的依赖
<!-- 兼容于thymeleaf的shiro --> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency>
注意:这里引用的版本是2.0.0,之前1.0.2有兼容问题
②ShiroConfig中加入配置
/** * 用于thymeleaf模板使用shiro标签 * @return */ @Bean public ShiroDialect shiroDialect() { return new ShiroDialect(); }
③前端页面使用shiro标签
<!--使用标签后 --> <shiro:hasRole name="dog"><a href="/dog">Dog</a></shiro:hasRole> <shiro:hasRole name="cat"><a href="/cat">Cat</a></shiro:hasRole> <hr> <shiro:hasPermission name="sing"><a href="/sing">Sing</a></shiro:hasPermission> <shiro:hasPermission name="jump"><a href="/jump">Jump</a></shiro:hasPermission> <shiro:hasPermission name="rap"><a href="/rap">Rap</a></shiro:hasPermission> <shiro:hasPermission name="basketball"><a href="/basketball">Basketball</a></shiro:hasPermission>
注意:使用前现在html标签内引入shiro标签,即<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="https://cache.yisu.com/upload/information/20210524/347/782630.gif
----------------------------------------------到这里,shiro授权基本搞定了---------------------------------------------------------
下面附上项目结构和代码:
image.png
AuthorizationController.java:
package com.cdq.shriodemo.controller; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.authz.annotation.RequiresRoles; import org.apache.shiro.subject.Subject; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class AuthorizationController { /** * 1.编程式: * (1)通过Subject的hasRole()进行角色的校检 * (2)通过isPermitted()进行权限的校检 */ /** * 1.注解式: * (1)使用注解@RequiresRoles("xxx")进行角色的校检 * (2)使用注解@RequiresPermissions("xxx")进行权限的校检 */ //一般对未授权用户需要统一返回指定403页面的,使用注解更加方便; // 需要做业务逻辑(比如对未授权的请求记录进行预警等),使用编程式更加方便。 //编程式 @GetMapping("/dog") public String dog() { Subject subject = SecurityUtils.getSubject(); if (subject.hasRole("dog")) { return "dog√"; } else { return "dog×"; } } //编程式 @GetMapping("/cat") public String cat() { Subject subject = SecurityUtils.getSubject(); if (subject.hasRole("cat")) { return "cat√"; } else { return "cat×"; } } @GetMapping("/sing") @RequiresRoles("cat")//如果subject中有cat角色才可以访问方法sing。如果没有这个权限则会抛出异常AuthorizationException。 public String sing() { return "sing"; } @GetMapping("/jump") @RequiresPermissions("jump")//要求subject中必须同时含有jump的权限才能执行方法jump()。 public String jump() { return "jump"; } //编程式 @GetMapping("/rap") public String rap() { Subject subject = SecurityUtils.getSubject(); if (subject.isPermitted("rap")) { return "rap"; } else { return "没权限你Rap个锤子啊!"; } } //编程式 @GetMapping("/basketball") public String basketball() { Subject subject = SecurityUtils.getSubject(); if (subject.isPermitted("basketball")) { return "basketball"; } else { return "你会打个粑粑球!"; } } }
CustomRealm.java:
package com.cdq.shriodemo.shiro; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import java.util.ArrayList; import java.util.List; /** * @Description: shiro的自定义realm * Realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。 * @author ChenDeQuan * @data 2019-05-22 17:51 */ public class CustomRealm extends AuthorizingRealm { @Override /** * 认证 */ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { //1.获取用户输入的账号 String username = (String)token.getPrincipal(); //2.通过username模拟从数据库中查找到user实体 User user = getUserByUserName(username); if(user == null){ return null; } //3.通过SimpleAuthenticationInfo做身份处理 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user,user.getPassword(),getName()); //4.用户账号状态验证等其他业务操作 if(!user.getAvailable()){ throw new AuthenticationException("该账号已经被禁用"); } //5.返回身份处理对象 return simpleAuthenticationInfo; } @Override /** * 授权 */ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) { //获取当前登录的用户 User user = (User) principal.getPrimaryPrincipal(); //通过SimpleAuthenticationInfo做授权 SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); //添加角色 simpleAuthorizationInfo.addRole(user.getRole()); //添加权限 simpleAuthorizationInfo.addStringPermissions(user.getPermissions()); return simpleAuthorizationInfo; } /** * 模拟通过username从数据库中查找到user实体 * @param username * @return */ private User getUserByUserName(String username){ List<User> users = getUsers(); for(User user : users){ if(user.getUsername().equals(username)){ return user; } } return null; } /** * 模拟数据库数据 * @return */ private List<User> getUsers(){ List<User> users = new ArrayList<>(2); List<String> cat = new ArrayList<>(2); cat.add("sing"); cat.add("rap"); List<String> dog = new ArrayList<>(2); dog.add("jump"); dog.add("basketball"); users.add(new User("cxk1","123",true,"cat",cat)); users.add(new User("cxk2","123",true,"dog",dog)); return users; } }
ShiroConfig.java:
package com.cdq.shriodemo.shiro; import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver; import java.util.LinkedHashMap; import java.util.Map; import java.util.Properties; /** * @Description springboot中的Shiro配置类 * @author ChenDeQuan * @data 2019-05-22 17:17 */ @Configuration public class ShiroConfig { /** * 配置Shiro核心 安全管理器 SecurityManager * SecurityManager安全管理器:所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;负责与后边介绍的其他组件进行交互。(类似于SpringMVC中的DispatcherServlet控制器) */ @Bean public SecurityManager securityManager(){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //将自定义的realm交给SecurityManager管理 securityManager.setRealm(new CustomRealm()); return securityManager; } /** * 配置Shiro的Web过滤器,拦截浏览器请求并交给SecurityManager处理 * @return */ @Bean public ShiroFilterFactoryBean webFilter(SecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //设置securityManager shiroFilterFactoryBean.setSecurityManager(securityManager); //配置拦截链 使用LinkedHashMap,因为LinkedHashMap是有序的,shiro会根据添加的顺序进行拦截 // Map<K,V> K指的是拦截的url V值的是该url是否拦截 Map<String,String> filterChainMap = new LinkedHashMap<String,String>(16); //配置退出过滤器logout,由shiro实现 filterChainMap.put("/logout","logout"); //authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问,先配置anon再配置authc。 filterChainMap.put("/login","anon"); filterChainMap.put("/**", "authc"); //设置默认登录的URL. shiroFilterFactoryBean.setLoginUrl("/login"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap); return shiroFilterFactoryBean; } /** * 开启aop注解支持 * 即在controller中使用 @RequiresPermissions("user/userList") */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){ AuthorizationAttributeSourceAdvisor attributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); //设置安全管理器 attributeSourceAdvisor.setSecurityManager(securityManager); return attributeSourceAdvisor; } @Bean @ConditionalOnMissingBean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator(); defaultAAP.setProxyTargetClass(true); return defaultAAP; } /** * 处理未授权的异常,返回自定义的错误页面(403) * @return */ @Bean public SimpleMappingExceptionResolver simpleMappingExceptionResolver() { SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver(); Properties properties = new Properties(); /*未授权处理页*/ properties.setProperty("UnauthorizedException", "403.html"); resolver.setExceptionMappings(properties); return resolver; } /** * 用于thymeleaf模板使用shiro标签 * @return */ @Bean public ShiroDialect shiroDialect() { return new ShiroDialect(); } }
User.java:
package com.cdq.shriodemo.shiro; import java.util.List; /** * @Description 用户 * @author ChenDeQuan * @data 2019-05-22 19:18 */ public class User { private String username; private String password; private Boolean available; private String role; private List<String> permissions; public User(String username, String password, Boolean available, String role, List<String> permissions) { this.username = username; this.password = password; this.available = available; this.role = role; this.permissions = permissions; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Boolean getAvailable() { return available; } public void setAvailable(Boolean available) { this.available = available; } public String getRole() { return role; } public void setRole(String role) { this.role = role; } public List<String> getPermissions() { return permissions; } public void setPermissions(List<String> permissions) { this.permissions = permissions; } }
success.html:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"> <head> <meta charset="UTF-8"> <title>success</title> </head> <body> <span th:text="'欢迎你,'+${username}"></span> <br> <!--使用标签前 --> <!--<a href="/dog">Dog</a>--> <!--<a href="/cat">Cat</a>--> <!--<hr>--> <!--<a href="/sing">Sing</a>--> <!--<a href="/jump">Jump</a>--> <!--<a href="/rap">Rap</a>--> <!--<a href="/basketball">Basketball</a>--> <!--<hr>--> <!--使用标签后 --> <shiro:hasRole name="dog"><a href="/dog">Dog</a></shiro:hasRole> <shiro:hasRole name="cat"><a href="/cat">Cat</a></shiro:hasRole> <hr> <shiro:hasPermission name="sing"><a href="/sing">Sing</a></shiro:hasPermission> <shiro:hasPermission name="jump"><a href="/jump">Jump</a></shiro:hasPermission> <shiro:hasPermission name="rap"><a href="/rap">Rap</a></shiro:hasPermission> <shiro:hasPermission name="basketball"><a href="/basketball">Basketball</a></shiro:hasPermission> </body> </html>
关于Springboot整合Shiro中怎样进行权限管理就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。