博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SpringBoot搭建基于Apache Shiro的权限管理功能
阅读量:5953 次
发布时间:2019-06-19

本文共 15408 字,大约阅读时间需要 51 分钟。

Shiro 是什么

Apache Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能: 

  • 认证 - 用户身份识别,常被称为用户“登录”;
  • 授权 - 访问控制;
  • 密码加密 - 保护或隐藏数据防止被偷窥;
  • 会话管理 - 每用户相关的时间敏感的状态。

对于任何一个应用程序,Shiro都可以提供全面的安全管理服务。并且相对于其他安全框架,Shiro要简单的多。

Shiro的架构介绍 

首先,来了解一下Shiro的三个核心组件:Subject, SecurityManager 和 Realms. 如下图: 

 
Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。 
Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。 
SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。 
Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。 
从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。 
Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。
Shiro完整架构图: 
 
除前文所讲Subject、SecurityManager 、Realm三个核心组件外,Shiro主要组件还包括: 
Authenticator :认证就是核实用户身份的过程。这个过程的常见例子是大家都熟悉的“用户/密码”组合。多数用户在登录软件系统时,通常提供自己的用户名(当事人)和支持他们的密码(证书)。如果存储在系统中的密码(或密码表示)与用户提供的匹配,他们就被认为通过认证。 
Authorizer :授权实质上就是访问控制 - 控制用户能够访问应用中的哪些内容,比如资源、Web页面等等。 
SessionManager :在安全框架领域,Apache Shiro提供了一些独特的东西:可在任何应用或架构层一致地使用Session API。即,Shiro为任何应用提供了一个会话编程范式 - 从小型后台独立应用到大型集群Web应用。这意味着,那些希望使用会话的应用开发者,不必被迫使用Servlet或EJB容器了。或者,如果正在使用这些容器,开发者现在也可以选择使用在任何层统一一致的会话API,取代Servlet或EJB机制。 
CacheManager :对Shiro的其他组件提供缓存支持。 

Shiro的使用

首先搭建一个springBoot项目

Maven Plugin添加Shiro相关jar包

1  
2
3
org.apache.shiro
4
shiro-core
5
${shiro.version}
6
7
8
org.apache.shiro
9
shiro-web
10
${shiro.version}
11
12
13
org.apache.shiro
14
shiro-spring
15
${shiro.version}
16
17
18
org.apache.shiro
19
shiro-ehcache
20
${shiro.version}
21
View Code

添加Shiro支持功能

1 package com.goku.webapi.config.Shiro; 2  3 import com.goku.webapi.mapper.ext.sysMenuExtMapper; 4 import com.goku.webapi.mapper.ext.sysUserExtMapper; 5 import com.goku.webapi.model.sysMenu; 6 import com.goku.webapi.model.sysRole; 7 import com.goku.webapi.model.sysUser; 8 import com.goku.webapi.service.sysUserService; 9 import org.apache.shiro.SecurityUtils;10 import org.apache.shiro.authc.*;11 import org.apache.shiro.authz.AuthorizationInfo;12 import org.apache.shiro.authz.SimpleAuthorizationInfo;13 import org.apache.shiro.realm.AuthorizingRealm;14 import org.apache.shiro.session.Session;15 import org.apache.shiro.subject.PrincipalCollection;16 import org.springframework.beans.factory.annotation.Autowired;17 18 /**19  * Created by nbfujx on 2017/11/7.20  */21 public class ShiroRealm extends AuthorizingRealm {22 23     @Autowired24     private sysUserExtMapper sysuserextmapper;25     @Autowired26     private sysMenuExtMapper sysmenuextmapper;27 28     /**29      *权限验证30      * **/31     @Override32     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {33         sysUser user = sysuserextmapper.selectByUsername((String) principalCollection.getPrimaryPrincipal());34         //把principals放session中 key=userId value=principals35         SecurityUtils.getSubject().getSession().setAttribute(String.valueOf(user.getId()),SecurityUtils.getSubject().getPrincipals());36         SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();37         //赋予角色38         for(sysRole userRole:user.getSysrole()){39             info.addRole(userRole.getRoleName());40         }41         //赋予权限42         for(sysMenu menu:sysmenuextmapper.selectByUserId(user.getId())){43             info.addStringPermission(menu.getPerms());44         }45 46         return info;47 48     }49 50     /**51      * 登录验证52      * **/53     @Override54     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {55         UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;56         String userName=token.getUsername();57         sysUser user = sysuserextmapper.selectByUsername(token.getUsername());58         if (user != null) {59             //设置用户session60             Session session = SecurityUtils.getSubject().getSession();61             session.setAttribute("user", user);62             return new SimpleAuthenticationInfo(userName,user.getPassword(),getName());63         } else {64             return null;65         }66     }67 }
View Code

添加Shiro配置文件

1 package com.goku.webapi.config.Shiro;  2   3 import org.apache.shiro.authc.credential.HashedCredentialsMatcher;  4 import org.apache.shiro.cache.ehcache.EhCacheManager;  5 import org.apache.shiro.spring.LifecycleBeanPostProcessor;  6 import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;  7 import org.apache.shiro.spring.web.ShiroFilterFactoryBean;  8 import org.apache.shiro.web.filter.authc.LogoutFilter;  9 import org.apache.shiro.web.mgt.DefaultWebSecurityManager; 10 import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; 11 import org.springframework.beans.factory.annotation.Qualifier; 12 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 13 import org.springframework.context.annotation.Bean; 14 import org.apache.shiro.mgt.SecurityManager; 15 import org.springframework.context.annotation.Configuration; 16 import org.springframework.context.annotation.DependsOn; 17  18 import javax.servlet.Filter; 19 import java.util.LinkedHashMap; 20 import java.util.Map; 21  22  23 /** 24  * shiro配置类 25  * Created by nbfujx on 2017/11/7. 26  */ 27 @Configuration 28 public class ShiroConfig { 29     /** 30      * LifecycleBeanPostProcessor,这是个DestructionAwareBeanPostProcessor的子类, 31      * 负责org.apache.shiro.util.Initializable类型bean的生命周期的,初始化和销毁。 32      * 主要是AuthorizingRealm类的子类,以及EhCacheManager类。 33      */ 34     @Bean(name = "lifecycleBeanPostProcessor") 35     public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { 36         return new LifecycleBeanPostProcessor(); 37     } 38  39  40     /** 41      * ShiroRealm,这是个自定义的认证类,继承自AuthorizingRealm, 42      * 负责用户的认证和权限的处理,可以参考JdbcRealm的实现。 43      */ 44     @Bean(name = "shiroRealm") 45     @DependsOn("lifecycleBeanPostProcessor") 46     public ShiroRealm shiroRealm() { 47         ShiroRealm realm = new ShiroRealm(); 48         realm.setCacheManager(ehCacheManager()); 49         return realm; 50     } 51  52     /** 53      * EhCacheManager,缓存管理,用户登陆成功后,把用户信息和权限信息缓存起来, 54      * 然后每次用户请求时,放入用户的session中,如果不设置这个bean,每个请求都会查询一次数据库。 55      */ 56     @Bean(name = "ehCacheManager") 57     @DependsOn("lifecycleBeanPostProcessor") 58     public EhCacheManager ehCacheManager() { 59         return new EhCacheManager(); 60     } 61  62     /** 63      * SecurityManager,权限管理,这个类组合了登陆,登出,权限,session的处理,是个比较重要的类。 64      */ 65     @Bean(name = "securityManager") 66     public DefaultWebSecurityManager securityManager() { 67         DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); 68         securityManager.setRealm(shiroRealm()); 69         securityManager.setCacheManager(ehCacheManager()); 70         return securityManager; 71     } 72  73  74     /** 75      * ShiroFilterFactoryBean,是个factorybean,为了生成ShiroFilter。 76      * 它主要保持了三项数据,securityManager,filters,filterChainDefinitionManager。 77      */ 78     @Bean(name = "shiroFilter") 79     public ShiroFilterFactoryBean shiroFilterFactoryBean() { 80         ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); 81         shiroFilterFactoryBean.setSecurityManager(securityManager()); 82  83         Map
filters = new LinkedHashMap
(); 84 shiroFilterFactoryBean.setFilters(filters); 85 86 87 Map
filterChainDefinitionManager = new LinkedHashMap
(); 88 filterChainDefinitionManager.put("/login", "anon"); 89 filterChainDefinitionManager.put("/logout", "anon"); 90 filterChainDefinitionManager.put("/sysUser/*", "authc,perms");//"authc,perms[sysUser:*]"); 91 filterChainDefinitionManager.put("/sysMenu/*", "authc,perms");//"authc,perms[sysUser:*]"); 92 filterChainDefinitionManager.put("/*", "anon"); 93 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionManager); 94 95 shiroFilterFactoryBean.setLoginUrl("/notAuthc"); 96 shiroFilterFactoryBean.setSuccessUrl("/"); 97 shiroFilterFactoryBean.setUnauthorizedUrl("/notAuthz"); 98 return shiroFilterFactoryBean; 99 }100 101 /**102 * DefaultAdvisorAutoProxyCreator,Spring的一个bean,由Advisor决定对哪些类的方法进行AOP代理。103 */104 @Bean105 @DependsOn("lifecycleBeanPostProcessor")106 public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {107 DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();108 defaultAAP.setProxyTargetClass(true);109 return defaultAAP;110 }111 112 /**113 * AuthorizationAttributeSourceAdvisor,shiro里实现的Advisor类,114 * 内部使用AopAllianceAnnotationsAuthorizingMethodInterceptor来拦截用以下注解的方法。115 */116 @Bean117 public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {118 AuthorizationAttributeSourceAdvisor aASA = new AuthorizationAttributeSourceAdvisor();119 aASA.setSecurityManager(securityManager());120 return aASA;121 }122 123 124 125 126 }
View Code

添加login验证类

1 package com.goku.webapi.controller.impl;  2   3 import com.alibaba.fastjson.JSON;  4 import com.goku.webapi.controller.loginController;  5 import com.goku.webapi.util.enums.returnCode;  6 import com.goku.webapi.util.message.returnMsg;  7 import org.apache.shiro.SecurityUtils;  8 import org.apache.shiro.authc.AuthenticationException;  9 import org.apache.shiro.authc.UsernamePasswordToken; 10 import org.apache.shiro.crypto.hash.Md5Hash; 11 import org.apache.shiro.session.SessionException; 12 import org.apache.shiro.subject.Subject; 13 import org.springframework.beans.factory.annotation.Autowired; 14 import org.springframework.boot.autoconfigure.web.ErrorAttributes; 15 import org.springframework.boot.autoconfigure.web.ErrorController; 16 import org.springframework.http.HttpStatus; 17 import org.springframework.web.bind.annotation.RequestMapping; 18 import org.springframework.web.bind.annotation.RequestMethod; 19 import org.springframework.web.bind.annotation.RequestParam; 20 import org.springframework.web.bind.annotation.RestController; 21 import org.springframework.web.context.request.RequestAttributes; 22 import org.springframework.web.context.request.ServletRequestAttributes; 23  24 import javax.servlet.http.HttpServletRequest; 25 import java.util.HashMap; 26 import java.util.Map; 27  28  29 /** 30  * Created by nbfujx on 2017-11-07. 31  */ 32 @RestController 33 public class loginControllerImpl implements loginController,ErrorController { 34  35  36     private final static String ERROR_PATH = "/error"; 37  38     @Autowired 39     private ErrorAttributes errorAttributes; 40  41     @RequestMapping(value = "/login", method = RequestMethod.GET)//测试方法 实际用post方法 42     public String login( 43             @RequestParam(value = "username", required = true) String userName, 44             @RequestParam(value = "password", required = true) String password, 45             @RequestParam(value = "rememberMe", required = true, defaultValue = "false") boolean rememberMe 46     ) { 47         String passwordmd5 = new Md5Hash(password, "2").toString(); 48         Subject subject = SecurityUtils.getSubject(); 49         UsernamePasswordToken token = new UsernamePasswordToken(userName, passwordmd5); 50         token.setRememberMe(rememberMe); 51         try { 52             subject.login(token); 53         } catch (AuthenticationException e) { 54             e.printStackTrace(); 55             return JSON.toJSONString (new  returnMsg(returnCode.ERROR)); 56         } 57         return  JSON.toJSONString (new  returnMsg(returnCode.SUCCESS)); 58     } 59  60  61     @Override 62     @RequestMapping(value = "/logout", method = RequestMethod.GET) 63     public String logout() { 64         Subject subject = SecurityUtils.getSubject(); 65         try { 66             subject.logout(); 67         }catch (SessionException e){ 68             e.printStackTrace(); 69             return JSON.toJSONString (new  returnMsg(returnCode.ERROR)); 70         } 71         return JSON.toJSONString (new  returnMsg(returnCode.SUCCESS)); 72     } 73  74     @Override 75     @RequestMapping(value = "/notAuthc", method = RequestMethod.GET) 76     public String notAuthc() { 77         return JSON.toJSONString (new  returnMsg(returnCode.NOTAUTHC)); 78     } 79  80     @Override 81     @RequestMapping(value = "/notAuthz", method = RequestMethod.GET) 82     public String notAuthz() { 83         return  JSON.toJSONString (new  returnMsg(returnCode.NOTAUTHZ)); 84     } 85  86     @Override 87     @RequestMapping(value =ERROR_PATH) 88     public String error(HttpServletRequest request) 89     { 90         Map
body = getErrorAttributes(request, getTraceParameter(request)); 91 return JSON.toJSONString (new returnMsg(returnCode.ERROR,body)); 92 } 93 94 @Override 95 public String getErrorPath() { 96 return ERROR_PATH; 97 } 98 99 private boolean getTraceParameter(HttpServletRequest request) {100 String parameter = request.getParameter("trace");101 if (parameter == null) {102 return false;103 }104 return !"false".equals(parameter.toLowerCase());105 }106 107 private Map
getErrorAttributes(HttpServletRequest request,boolean includeStackTrace) {108 RequestAttributes requestAttributes = new ServletRequestAttributes(request);109 Map
map = this.errorAttributes.getErrorAttributes(requestAttributes,includeStackTrace);110 String URL = request.getRequestURL().toString();111 map.put("URL", URL);112 return map;113 }114 115 }
View Code

添加业务类,业务方法增加RequiresPermissions权限注解

有权限

1 @Override2     @RequestMapping(value="getUser/{id}", method = RequestMethod.GET)3     @RequiresPermissions(value={"sysUser:selectByid"})4     public String  selectByid(@PathVariable String id) {5         this.logger.info("selectByid");6         return JSON.toJSONString (new returnMsg(returnCode.SUCCESS,sysuserService.selectByid(id)));7     }
View Code

无权限

1 @Override2     @RequestMapping(value="getMenu/{id}", method = RequestMethod.GET)3     @RequiresPermissions(value={"sysMenu:selectByid"})4     public String  selectByid(@PathVariable long id) {5         return JSON.toJSONString (new returnMsg(returnCode.SUCCESS,sysmenuService.selectByid(id)));6     }
View Code

Shiro的使用验证

首先我们访问未登录时的资源

进行登录

我们访问登录后有权限的资源

 

我们访问登录后没有权限的资源

 

 

我们可以对每个方法增加权限控制

GITHUB

github : 

转载地址:http://yqoxx.baihongyu.com/

你可能感兴趣的文章
以太坊本地开发环境搭建
查看>>
图说 WebAssembly(二):JIT 编译器
查看>>
多线程设计模式 - Future模式
查看>>
android清理缓存动画、天气APP、购物下单选择器、阅读APP、饿了么demo等源码
查看>>
驳《我不是很懂 Node.js 社区的 DRY 文化》
查看>>
《Spring In Action(第4版)》阅读总结(四)渲染Web视图
查看>>
CGI、PHP-CGI、FastCGI
查看>>
PhoneGap极光推送 cordova消息推送
查看>>
Subarray Sum Equals K
查看>>
preventDefault, stopPropagation, stopImmediatePropagation 三者的区别
查看>>
王下邀月熊_Chevalier的前端每周清单系列文章索引
查看>>
js设计模式之观察者模式和发布/订阅模式
查看>>
Mac上以太坊Geth环境安装
查看>>
基于Netty模拟解析Binlog
查看>>
零配置构建工具:parcel
查看>>
算法题解:找出包含给定字符的最小窗口(枚举的一般方法)
查看>>
lodash源码分析之baseFindIndex中的运算符优先级
查看>>
MySQL数据库规范及解读
查看>>
用 Go 构建一个区块链 -- Part 5: 地址
查看>>
vscode+sftp 开发模式环境的同步
查看>>