Spring安全框架Shiro

Shiro的介绍

 Apache Shiro是一个强大易用的Java安全框架,它提供的主要功能有:

     认证 -——用户身份识别,常被称为用户“登录”;

      授权—— 访问控制;

密码加密——保护或隐藏数据防止被偷窥;

会话管理——每用户相关的时间敏感的状态。

Shiro的三个核心组件(Subject,SecurityManager 和 Realms)介绍

Subject:“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。 Subject代表的是当前用户的安全操作。

SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。

Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。(当配置Shiro时,你至少要指定一个Realm,用于认证和(或)授权,至少需要一个。 Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。)

下图为Shiro功能模块结构:

 

这些模块各有作用:

 

Authentication:身份认证/登录,验证用户是不是拥有相应的身份;

Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;

Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;

Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

Web Support:Web支持,可以非常容易的集成到Web环境;

Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;

Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;

Testing:提供测试支持;

Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;

Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。

Shiro依赖包

maven环境下,pom.xml中依赖包配置:

[html]  view plain  copy
  1. <dependency>    
  2.     <groupId>org.apache.shiro</groupId>    
  3.     <artifactId>shiro-core</artifactId>    
  4.     <version>1.2.3</version>    
  5. </dependency>    
  6. <dependency>    
  7.     <groupId>org.apache.shiro</groupId>    
  8.     <artifactId>shiro-web</artifactId>    
  9.     <version>1.2.3</version>    
  10. </dependency>    
  11. <dependency>    
  12.     <groupId>org.apache.shiro</groupId>    
  13.     <artifactId>shiro-ehcache</artifactId>    
  14.     <version>1.2.3</version>    
  15. </dependency>    
  16. <dependency>    
  17.     <groupId>org.apache.shiro</groupId>    
  18.     <artifactId>shiro-spring</artifactId>    
  19.     <version>1.2.3</version>    
  20. </dependency>    

web工程中引入Shiro框架,首先要在web.xml中配置:

[html]  view plain  copy
  1. <!-- Apache Shiro -->  
  2.   
  3.   <context-param>  
  4.       <param-name>contextConfigLocation</param-name>  
  5.       <param-value>classpath:application.xml,classpath:shiro/spring-shiro.xml</param-value>  
  6.   </context-param>  
  7.   
  8.  <filter>  
  9. <filter-name>shiroFilter</filter-name>  
  10. <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
  11. <init-param>  
  12. <param-name>targetFilterLifecycle</param-name>  
  13. <param-value>true</param-value>  
  14. </init-param>  
  15. </filter>  
  16. <filter-mapping><filter-name>shiroFilter</filter-name>  
  17. <url-pattern>/*</url-pattern>  
  18. </filter-mapping>  

web程序启动时,首先会加载spring-shiro.xml配置文件,然后执行web中的过滤器,实现安全登录。

配置Realm,进行验证及授权

定义该一个安全认证的实现类,需要继承AuthorizingRealm并实现登录验证和赋予角色权限的两个方法

即:

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationTokenauthcToken);--------登录认证时使用

protected AuthorizationInfogetAuthorizationInfo(PrincipalCollection principals);---------用户授权时使用

还可以自定义一些其他业务中使用到的方法,如下:

[java]  view plain  copy
  1. @SuppressWarnings("restriction")  
  2. @Service  
  3. //@DependsOn({"userDao","roleDao","menuDao"})  
  4. public class SystemAuthorizingRealm extends AuthorizingRealm {  
  5.   
  6.     private Logger logger = LoggerFactory.getLogger(getClass());  
  7.       
  8.     private SystemService systemService;  
  9.       
  10.     public SystemAuthorizingRealm() {  
  11.         this.setCachingEnabled(false);  
  12.     }  
  13.   
  14.     /** 
  15.      * 认证回调函数, 登录时调用 
  16.      */  
  17.     @Override  
  18.     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) {  
  19.         UsernamePasswordToken token = (UsernamePasswordToken) authcToken;  
  20.           
  21.         int activeSessionSize = getSystemService().getSessionDao().getActiveSessions(false).size();  
  22.         if (logger.isDebugEnabled()){  
  23.             logger.debug("login submit, active session size: {}, username: {}", activeSessionSize, token.getUsername());  
  24.         }  
  25.           
  26.         // 校验登录验证码  
  27.         if (LoginController.isValidateCodeLogin(token.getUsername(), falsefalse)){  
  28.             Session session = UserUtils.getSession();  
  29.             String code = (String)session.getAttribute(ValidateCodeServlet.VALIDATE_CODE);  
  30.             if (token.getCaptcha() == null || !token.getCaptcha().toUpperCase().equals(code)){  
  31.                 throw new AuthenticationException("msg:验证码错误, 请重试.");  
  32.             }  
  33.         }  
  34.           
  35.         // 校验用户名密码  
  36.         User user = getSystemService().getUserByLoginName(token.getUsername());  
  37.         if (user != null) {  
  38.             if (Global.NO.equals(user.getLoginFlag())){  
  39.                 throw new AuthenticationException("msg:该已帐号禁止登录.");  
  40.             }  
  41.             byte[] salt = Encodes.decodeHex(user.getPassword().substring(0,16));  
  42.             return new SimpleAuthenticationInfo(new Principal(user, token.isMobileLogin()),   
  43.                     user.getPassword().substring(16), ByteSource.Util.bytes(salt), getName());  
  44.         } else {  
  45.             return null;  
  46.         }  
  47.     }  
  48.       
  49.     /** 
  50.      * 获取权限授权信息,如果缓存中存在,则直接从缓存中获取,否则就重新获取, 登录成功后调用 
  51.      */  
  52.     protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {  
  53.         if (principals == null) {  
  54.             return null;  
  55.         }  
  56.           
  57.         AuthorizationInfo info = null;  
  58.   
  59.         info = (AuthorizationInfo)UserUtils.getCache(UserUtils.CACHE_AUTH_INFO);  
  60.   
  61.         if (info == null) {  
  62.             info = doGetAuthorizationInfo(principals);  
  63.             if (info != null) {  
  64.                 UserUtils.putCache(UserUtils.CACHE_AUTH_INFO, info);  
  65.             }  
  66.         }  
  67.   
  68.         return info;  
  69.     }  
  70.   
  71.     /** 
  72.      * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用 
  73.      */  
  74.     @Override  
  75.     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {  
  76.         Principal principal = (Principal) getAvailablePrincipal(principals);  
  77.         // 获取当前已登录的用户  
  78.         if (!Global.TRUE.equals(Global.getConfig("user.multiAccountLogin"))){  
  79.             Collection<Session> sessions = getSystemService().getSessionDao().getActiveSessions(true, principal, UserUtils.getSession());  
  80.             if (sessions.size() > 0){  
  81.                 // 如果是登录进来的,则踢出已在线用户  
  82.                 if (UserUtils.getSubject().isAuthenticated()){  
  83.                     for (Session session : sessions){  
  84.                         getSystemService().getSessionDao().delete(session);  
  85.                     }  
  86.                 }  
  87.                 // 记住我进来的,并且当前用户已登录,则退出当前用户提示信息。  
  88.                 else{  
  89.                     UserUtils.getSubject().logout();  
  90.                     throw new AuthenticationException("msg:账号已在其它地方登录,请重新登录。");  
  91.                 }  
  92.             }  
  93.         }  
  94.         User user = getSystemService().getUserByLoginName(principal.getLoginName());  
  95.         if (user != null) {  
  96.             SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();  
  97.             List<Menu> list = UserUtils.getMenuList();  
  98.             for (Menu menu : list){  
  99.                 if (StringUtils.isNotBlank(menu.getPermission())){  
  100.                     // 添加基于Permission的权限信息  
  101.                     for (String permission : StringUtils.split(menu.getPermission(),",")){  
  102.                         info.addStringPermission(permission);  
  103.                     }  
  104.                 }  
  105.             }  
  106.             // 添加用户权限  
  107.             info.addStringPermission("user");  
  108.             // 添加用户角色信息  
  109.             for (Role role : user.getRoleList()){  
  110.                 info.addRole(role.getEnname());  
  111.             }  
  112.             // 更新登录IP和时间  
  113.             getSystemService().updateUserLoginInfo(user);  
  114.             // 记录登录日志  
  115.             LogUtils.saveLog(Servlets.getRequest(), "系统登录");  
  116.             return info;  
  117.         } else {  
  118.             return null;  
  119.         }  
  120.     }  
  121.       
  122.     @Override  
  123.     protected void checkPermission(Permission permission, AuthorizationInfo info) {  
  124.         authorizationValidate(permission);  
  125.         super.checkPermission(permission, info);  
  126.     }  
  127.       
  128.     @Override  
  129.     protected boolean[] isPermitted(List<Permission> permissions, AuthorizationInfo info) {  
  130.         if (permissions != null && !permissions.isEmpty()) {  
  131.             for (Permission permission : permissions) {  
  132.                 authorizationValidate(permission);  
  133.             }  
  134.         }  
  135.         return super.isPermitted(permissions, info);  
  136.     }  
  137.       
  138.     @Override  
  139.     public boolean isPermitted(PrincipalCollection principals, Permission permission) {  
  140.         authorizationValidate(permission);  
  141.         return super.isPermitted(principals, permission);  
  142.     }  
  143.       
  144.     @Override  
  145.     protected boolean isPermittedAll(Collection<Permission> permissions, AuthorizationInfo info) {  
  146.         if (permissions != null && !permissions.isEmpty()) {  
  147.             for (Permission permission : permissions) {  
  148.                 authorizationValidate(permission);  
  149.             }  
  150.         }  
  151.         return super.isPermittedAll(permissions, info);  
  152.     }  
  153.       
  154.     /** 
  155.      * 授权验证方法 
  156.      * @param permission 
  157.      */  
  158.     private void authorizationValidate(Permission permission){  
  159.         // 模块授权预留接口  
  160.     }  
  161.       
  162.     /** 
  163.      * 设定密码校验的Hash算法与迭代次数 
  164.      */  
  165.     @PostConstruct  
  166.     public void initCredentialsMatcher() {  
  167.         HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(SystemService.HASH_ALGORITHM);  
  168.         matcher.setHashIterations(SystemService.HASH_INTERATIONS);  
  169.         setCredentialsMatcher(matcher);  
  170.     }  
  171.   
  172.     /** 
  173.      * 获取系统业务对象 
  174.      */  
  175.     public SystemService getSystemService() {  
  176.         if (systemService == null){  
  177.             systemService = SpringContextHolder.getBean(SystemService.class);  
  178.         }  
  179.         return systemService;  
  180.     }  
  181.       
  182.     /** 
  183.      * 授权用户信息 
  184.      */  
  185.     public static class Principal implements Serializable {  
  186.   
  187.         private static final long serialVersionUID = 1L;  
  188.           
  189.         private String id; // 编号  
  190.         private String loginName; // 登录名  
  191.         private String name; // 姓名  
  192.         private boolean mobileLogin; // 是否手机登录  
  193.           
  194. //      private Map<String, Object> cacheMap;  
  195.   
  196.         public Principal(User user, boolean mobileLogin) {  
  197.             this.id = user.getId();  
  198.             this.loginName = user.getLoginName();  
  199.             this.name = user.getName();  
  200.             this.mobileLogin = mobileLogin;  
  201.         }  
  202.   
  203.         public String getId() {  
  204.             return id;  
  205.         }  
  206.   
  207.         public String getLoginName() {  
  208.             return loginName;  
  209.         }  
  210.   
  211.         public String getName() {  
  212.             return name;  
  213.         }  
  214.   
  215.         public boolean isMobileLogin() {  
  216.             return mobileLogin;  
  217.         }  
  218.   
  219.         /** 
  220.          * 获取SESSIONID 
  221.          */  
  222.         public String getSessionid() {  
  223.             try{  
  224.                 return (String) UserUtils.getSession().getId();  
  225.             }catch (Exception e) {  
  226.                 return "";  
  227.             }  
  228.         }  
  229.           
  230.         @Override  
  231.         public String toString() {  
  232.             return id;  
  233.         }  
  234.   
  235.     }  
  236. }  

Shiro配置文件

[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3.     xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="  
  4.         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd  
  5.         http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-4.1.xsd"  
  6.     default-lazy-init="true">  
  7.   
  8.     <description>Shiro Configuration</description>  
  9.   
  10.     <!-- 加载配置属性文件 -->  
  11.     <context:property-placeholder ignore-unresolvable="true" location="classpath:jeesite.properties" />  
  12.       
  13.     <!-- Shiro权限过滤过滤器定义 -->  
  14.     <bean name="shiroFilterChainDefinitions" class="java.lang.String">  
  15.         <constructor-arg>  
  16.             <value>  
  17.                 /static/** = anon  
  18.                 /userfiles/** = anon  
  19.                 ${adminPath}/cas = cas  
  20.                 ${adminPath}/login = authc  
  21.                 ${adminPath}/logout = logout  
  22.                 ${adminPath}/** = user  
  23.                 /act/editor/** = user  
  24.                 /ReportServer/** = user  
  25.             </value>  
  26.         </constructor-arg>  
  27.     </bean>  
  28.       
  29.     <!-- 安全认证过滤器 -->  
  30.     <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
  31.         <property name="securityManager" ref="securityManager" />  
  32.         <property name="loginUrl" value="${adminPath}/login" />  
  33.         <property name="successUrl" value="${adminPath}?login" />  
  34.         <property name="filters">  
  35.             <map>  
  36.                 <entry key="cas" value-ref="casFilter"/>  
  37.                 <entry key="authc" value-ref="formAuthenticationFilter"/>  
  38.             </map>  
  39.         </property>  
  40.         <property name="filterChainDefinitions">  
  41.             <ref bean="shiroFilterChainDefinitions"/>  
  42.         </property>  
  43.     </bean>  
  44.       
  45.     <!-- CAS认证过滤器 -->    
  46.     <bean id="casFilter" class="org.apache.shiro.cas.CasFilter">    
  47.         <property name="failureUrl" value="${adminPath}/login"/>  
  48.     </bean>  
  49.       
  50.     <!-- 定义Shiro安全管理配置 -->  
  51.     <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">  
  52.         <property name="realm" ref="systemAuthorizingRealm" />  
  53.         <property name="sessionManager" ref="sessionManager" />  
  54.         <property name="cacheManager" ref="shiroCacheManager" />  
  55.     </bean>  
  56.       
  57.     <!-- 自定义会话管理配置 -->  
  58.     <bean id="sessionManager" class="com.thinkgem.jeesite.common.security.shiro.session.SessionManager">   
  59.         <property name="sessionDAO" ref="sessionDAO"/>  
  60.           
  61.         <!-- 会话超时时间,单位:毫秒  -->  
  62.         <property name="globalSessionTimeout" value="${session.sessionTimeout}"/>  
  63.           
  64.         <!-- 定时清理失效会话, 清理用户直接关闭浏览器造成的孤立会话   -->  
  65.         <property name="sessionValidationInterval" value="${session.sessionTimeoutClean}"/>  
  66. <!--         <property name="sessionValidationSchedulerEnabled" value="false"/> -->  
  67.         <property name="sessionValidationSchedulerEnabled" value="true"/>  
  68.           
  69.         <property name="sessionIdCookie" ref="sessionIdCookie"/>  
  70.         <property name="sessionIdCookieEnabled" value="true"/>  
  71.     </bean>  
  72.       
  73.     <!-- 指定本系统SESSIONID, 默认为: JSESSIONID 问题: 与SERVLET容器名冲突, 如JETTY, TOMCAT 等默认JSESSIONID,  
  74.         当跳出SHIRO SERVLET时如ERROR-PAGE容器会为JSESSIONID重新分配值导致登录会话丢失! -->  
  75.     <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">  
  76.         <constructor-arg name="name" value="jeesite.session.id"/>  
  77.     </bean>  
  78.   
  79.     <!-- 自定义Session存储容器 -->  
  80.     <bean id="sessionDAO" class="com.thinkgem.jeesite.common.security.shiro.session.CacheSessionDAO">  
  81.         <property name="sessionIdGenerator" ref="idGen" />  
  82.         <property name="activeSessionsCacheName" value="activeSessionsCache" />  
  83.         <property name="cacheManager" ref="shiroCacheManager" />  
  84.     </bean>  
  85.       
  86.     <!-- 自定义系统缓存管理器-->  
  87.     <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">  
  88.         <property name="cacheManager" ref="cacheManager"/>  
  89.     </bean>  
  90.       
  91.     <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->  
  92.     <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>  
  93.       
  94.     <!-- AOP式方法级权限检查  -->  
  95.     <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">  
  96.         <property name="proxyTargetClass" value="true" />  
  97.     </bean>  
  98.     <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">  
  99.         <property name="securityManager" ref="securityManager"/>  
  100.     </bean>  
  101.       
  102. </beans>  

这里做一下说明,Shiro默认到的权限验证类别:

anon --org.apache.shiro.web.filter.authc.AnonymousFilter

authc -- org.apache.shiro.web.filter.authc.FormAuthenticationFilter

authcBasic --org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter

perms --org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter

port --org.apache.shiro.web.filter.authz.PortFilter

rest --org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter

roles --org.apache.shiro.web.filter.authz.RolesAuthorizationFilter

ssl --org.apache.shiro.web.filter.authz.SslFilter

user --org.apache.shiro.web.filter.authc.UserFilter

logout --org.apache.shiro.web.filter.authc.LogoutFilter

 解释:

anon---例子/admins/**=anon没有参数,表示可以匿名使用。

authc---例子/admins/user/**=authc表示需要认证(登录)才能使用,没有参数

roles---例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。

perms---例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。

rest---例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method],其中method为post,get,delete等。

port---例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?后面的参数。

authcBasic---例如/admins/user/**=authcBasic没有参数表示httpBasic认证

ssl---例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https

user---例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查

注意:

Shiro.xml加载配置是从上而下的,也就是向上面的配置,如/** = anon  ,如果把这个配置在第一行,那么下面的配置都没用。因为是从上往下去匹配,只要匹配中了,就不匹配了所以必须要有序。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值