本文共 15408 字,大约阅读时间需要 51 分钟。
Apache Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能:
对于任何一个应用程序,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的其他组件提供缓存支持。1 23 7org.apache.shiro 4shiro-core 5${shiro.version} 68 12org.apache.shiro 9shiro-web 10${shiro.version} 1113 17org.apache.shiro 14shiro-spring 15${shiro.version} 1618 org.apache.shiro 19shiro-ehcache 20${shiro.version} 21
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 }
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 Mapfilters = 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 }
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 Mapbody = 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 }
有权限
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 }
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 }
我们可以对每个方法增加权限控制
github :
转载地址:http://yqoxx.baihongyu.com/