zoukankan      html  css  js  c++  java
  • Shiro 整合SpringMVC 并且实现权限管理

            Apache Shiro是Java的一个安全框架。目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可能没有Spring Security做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的Shiro就足够了。


      因为我总结的是使用SpringMVC和Apache Shiro整合,注重的是整合和使用,至于基础,我这里就不细说了.按照惯例,既然是需要创建项目,那么我们首先需要JAR包,Apache shiro的架包除了除了基本的以外,我们还需要shiro-web和shiro-spring的的架包,下面是所需要的所有shiro架包,至于其他的架包,像缓存的架包,Spring和SpringMVC的架包还是和我们以前使用的架包一样的。

    复制代码
    <dependency>  
            <groupId>org.apache.shiro</groupId>  
            <artifactId>shiro-core</artifactId>  
            <version>1.2.3</version>  
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.2.3</version>
        </dependency>
    复制代码

      所有的架包都搞清楚了以后,我们就可以开始正式搭建了,在myeclise中创建一个maven项目,将需要的架包信息依赖全部放入。下面就分步骤来创建
      1.首先创建spring的配置文件,位置都在在resource中,配置文件是spring-context.xml,创建Apache Shiro的配置文件,名字是spring-context-shiro.xml,还有一个配置文件是springmvc的,配置文件是spring-mvc,这样起名是有原因的,因为这样我们就可以在web.xml中设置配置文件的时候,直接使用通配符了:
      

    复制代码
    1. <!-- 配置spring容器的路径 -->
    2. <context-param>
    3. <param-name>contextConfigLocation</param-name>
    4. <param-value>classpath*:/spring-context-*.xml</param-value>
    5. </context-param>
    6. <!-- 对spring开始监听 -->
    7. <listener>
    8. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    9. </listener>
    复制代码

    这样就可以扫描到两个配置文件了,又不会扫描到我们的spring-mvc.xml了,

    2除了在web.xml中设置这个以外,我们还需要设置spring-mvc的位置:

    复制代码
    1. <!-- MVC Servlet
    2. 设置springmvc的Servlet
    3. -->
    4. <servlet>
    5. <servlet-name>springServlet</servlet-name>
    6. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    7. <init-param>
    8. <param-name>contextConfigLocation</param-name>
    9. <param-value>classpath:springmvc.xml</param-value>
    10. </init-param>
    11. <load-on-startup>1</load-on-startup>
    12. </servlet>
    13. <servlet-mapping>
    14. <servlet-name>springServlet</servlet-name>
    15. <url-pattern>/</url-pattern>
    16. </servlet-mapping>

    复制代码

    3.在web.xml中配置shiroFilter:

    复制代码
    1. <filter>
    2. <filter-name>shiroFilter</filter-name>
    3. <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    4. </filter>
    5. <filter-mapping>
    6. <filter-name>shiroFilter</filter-name>
    7. <url-pattern>/*</url-pattern>
    8. </filter-mapping>
    复制代码

    注意,这个shiroFilter名称,后面的配置还需要使用到,所以要注意咯。
    4,因为shiro的session是自己实现的,所以我们还需要一个缓存框架,所以在spring的配置文件一定要注意配置哦,

    1. <!-- 缓存 -->
    2. <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
    3. <property name="configLocation" value="classpath:${ehcache.file}"></property>
    4. </bean>

    spring的其他的配置,该怎样还是这样,我们的重点是配置spring-context-shiro.xml:先把配置的贴出来,然后讲一下这几个配置的意义:

      

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

    复制代码

    ecurityManager:shiro最重要的一个对象,授权和验证都是由它来做的,下面就一一的来讲他的依赖类,

    一:realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。下对于源代码,我就不细细的研究了,下面是我重写的realm,:

    复制代码
    package com.yonyou.hotusm.module.sys.security;
    
     
    
    import java.io.Serializable;
    
     
    
    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.authc.UsernamePasswordToken;
    
    import org.apache.shiro.authz.AuthorizationInfo;
    
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    
    import org.apache.shiro.authz.UnauthenticatedException;
    
    import org.apache.shiro.realm.AuthorizingRealm;
    
    import org.apache.shiro.subject.PrincipalCollection;
    
    import org.springframework.beans.factory.annotation.Autowired;
    
    import org.springframework.stereotype.Service;
    
     
    
    import com.yonyou.hotusm.module.sys.dao.UserDao;
    
    import com.yonyou.hotusm.module.sys.entity.User;
    
    import com.yonyou.hotusm.module.sys.util.UserUtils;
    
     
    
    @Service
    
    public class SystemAuthorizingRealm extends AuthorizingRealm{
    
     
    
    @Autowired
    
    private UserDao userDao;
    
    @Override
    
    protected AuthorizationInfo doGetAuthorizationInfo(
    
    PrincipalCollection principals) {
    
    SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
    
    info.addStringPermission("sys:manager");
    
    info.addStringPermission("user");
    
    System.out.println("开始授权");
    
    return info;
    
    }
    
    
    @Override
    
    protected AuthenticationInfo doGetAuthenticationInfo(
    
    AuthenticationToken token) throws AuthenticationException {
    
    UsernamePasswordToken upToken=(UsernamePasswordToken) token; 
    
    String username=upToken.getUsername();
    
    String password=new String(upToken.getPassword());
    
    User user=new User();
    
    user.setLoginName(username);
    
    user=userDao.get(user);
    
    System.out.println("===========");
    
    if(user!=null){
    
    if(user.getPassword().equals(password)){
    
    return new SimpleAuthenticationInfo(username,password,getName());
    
    }
    
    }
    
    throw new UnauthenticatedException();
    
     
    
    }
    
    public static class Principal implements Serializable {
    
    
    private static final long serialVersionUID = 1L;
    
    
    private String id; // 编号private String loginName; // 登录名private String name; // 姓名public Principal(User user) {
    
    this.id = user.getId();
    
    this.loginName = user.getLoginName();
    
    this.name = user.getName();
    
    }
    
    
    public String getId() {
    
    return id;
    
    }
    
    
    public String getLoginName() {
    
    return loginName;
    
    }
    
     
    
    public String getName() {
    
    return name;
    
    }
     
    
    /**
    
     * 获取SESSIONID
    
     */public String getSessionid() {
    
    try{
    
    return (String) UserUtils.getSession().getId();
    
    }catch (Exception e) {
    
    return "";
    
    }
    
    }
    
    @Override
    
    public String toString() {
    
    return id;
    
    }
    
    }
    
    }
    复制代码

     

    看的出来,其中最重要的是doGetAuthorizationInfo和doGetAuthenticationInfo,这两个方法,doGetAuthorizationInfo是对当前的用户进行授权的,至于授权的时期,就是当用户需要验证的时候,我这里只是简单的写死了,但是在实际项目开发中,我们一般会将权限存放在数据表中,所以真实情况是先到数据库中查出一个集合,然后迭代授权,

      doGetAuthenticationInfo对于的是对用户验证,这里我们就需要从数据库中根据用户查出用户,根据用户情况,抛出不用的异常。

    下面就是讲解sessionManager,因为Shiro有自己的一套session体系,有sessionManager就不奇怪了,sessionManager主要职责是管理session的创建和删除,特别提一下,sessionManagersession的操作,其实只是调用了sessionDAO,然再加上自己的一些操作,所以,我们可以看到sessionManagerbean还依赖sessionDAO,下面是自己实现的sessionManager

    复制代码
    package com.yonyou.hotsum.common.security.shiro.session;
    
     
    
    import java.io.Serializable;
    
    import java.util.Collection;
    
    import java.util.Date;
    
     
    
    import javax.servlet.ServletRequest;
    
    import javax.servlet.ServletResponse;
    
    import javax.servlet.http.HttpServletRequest;
    
    import javax.servlet.http.HttpServletResponse;
    
     
    
    import org.apache.shiro.session.InvalidSessionException;
    
    import org.apache.shiro.session.Session;
    
    import org.apache.shiro.session.UnknownSessionException;
    
    import org.apache.shiro.session.mgt.SessionContext;
    
    import org.apache.shiro.session.mgt.SessionKey;
    
    import org.apache.shiro.session.mgt.SimpleSession;
    
    import org.apache.shiro.web.servlet.Cookie;
    
    import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
    
    import org.apache.shiro.web.servlet.SimpleCookie;
    
    import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
    
    import org.apache.shiro.web.util.WebUtils;
    
     
    
    /**
    
     * 自定义WEB会话管理类
    
     * @author hotusm
    
     * 
    
     */public class SessionManager extends DefaultWebSessionManager {
    
     
    
    public SessionManager() {
    
    super();
    
    }
    
     
    
    @Override
    
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
    
    // 如果参数中包含“__sid”参数,则使用此sid会话。 例如:http://localhost/project?__sid=xxx&__cookie=true
    String sid = request.getParameter("__sid");
    
    if (org.apache.commons.lang3.StringUtils.isNotBlank(sid)) {
    
    // 是否将sid保存到cookie,浏览器模式下使用此参数。if (WebUtils.isTrue(request, "__cookie")){
    
            HttpServletRequest rq = (HttpServletRequest)request;
    
            HttpServletResponse rs = (HttpServletResponse)response;
    
    Cookie template = getSessionIdCookie();
    
            Cookie cookie = new SimpleCookie(template);
    
    cookie.setValue(sid); cookie.saveTo(rq, rs);
    
    }
    
    // 设置当前session状态
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
    
                        ShiroHttpServletRequest.URL_SESSION_ID_SOURCE); // session来源与url
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sid);
    
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
    
             return sid;
    
    }else{
    
    return super.getSessionId(request, response);
    
    }
    
    }
    
     
    
    @Override
    
    public void validateSessions() {
    
    super.validateSessions();
    
    }
    
     
    
    @Override
    
    protected Session retrieveSession(SessionKey sessionKey) {
    
    try{
    
    return super.retrieveSession(sessionKey);
    
    }catch (UnknownSessionException e) {
    
         // 获取不到SESSION不抛出异常return null;
    
    }
    
    }
    
     
    
        @Override
    
    public Date getStartTimestamp(SessionKey key) {
    
         try{
    
         return super.getStartTimestamp(key);
    
         }catch (InvalidSessionException e) {
    
         // 获取不到SESSION不抛出异常return null;
    
    }
    
        }
    
     
    
        @Override
    
    public Date getLastAccessTime(SessionKey key) {
    
         try{
    
         return super.getLastAccessTime(key);
    
         }catch (InvalidSessionException e) {
    
         // 获取不到SESSION不抛出异常return null;
    
    }
    
        }
    
     
    
        @Override
    
    public long getTimeout(SessionKey key){
    
         try{
    
         return super.getTimeout(key);
    
         }catch (InvalidSessionException e) {
    
         // 获取不到SESSION不抛出异常return 0;
    
    }
    
        }
    
     
    
        @Override
    
    public void setTimeout(SessionKey key, long maxIdleTimeInMillis) {
    
         try{
    
         super.setTimeout(key, maxIdleTimeInMillis);
    
         }catch (InvalidSessionException e) {
    
         // 获取不到SESSION不抛出异常
    }
    
        }
    
     
    
        @Override
    
    public void touch(SessionKey key) {
    
         try{
    
         super.touch(key);
    
    }catch (InvalidSessionException e) {
    
    // 获取不到SESSION不抛出异常
    }
    
        }
    
     
    
        @Override
    
    public String getHost(SessionKey key) {
    
         try{
    
         return super.getHost(key);
    
         }catch (InvalidSessionException e) {
    
         // 获取不到SESSION不抛出异常return null;
    
    }
    
        }
    
     
    
        @Override
    
    public Collection<Object> getAttributeKeys(SessionKey key) {
    
         try{
    
         return super.getAttributeKeys(key);
    
         }catch (InvalidSessionException e) {
    
         // 获取不到SESSION不抛出异常return null;
    
    }
    
        }
    
     
    
        @Override
    
    public Object getAttribute(SessionKey sessionKey, Object attributeKey) {
    
         try{
    
         return super.getAttribute(sessionKey, attributeKey);
    
         }catch (InvalidSessionException e) {
    
         // 获取不到SESSION不抛出异常return null;
    
    }
    
        }
    
     
    
        @Override
    
    public void setAttribute(SessionKey sessionKey, Object attributeKey, Object value) {
    
         try{
    
         super.setAttribute(sessionKey, attributeKey, value);
    
         }catch (InvalidSessionException e) {
    
         // 获取不到SESSION不抛出异常
    }
    
        }
    
     
    
        @Override
    
    public Object removeAttribute(SessionKey sessionKey, Object attributeKey) {
    
         try{
    
         return super.removeAttribute(sessionKey, attributeKey);
    
         }catch (InvalidSessionException e) {
    
         // 获取不到SESSION不抛出异常return null;
    
    }
    
        }
    
     
    
        @Override
    
    public void stop(SessionKey key) {
    
         try{
    
         super.stop(key);
    
         }catch (InvalidSessionException e) {
    
         // 获取不到SESSION不抛出异常
    }
    
        }
    
        
    
        @Override
    
    public void checkValid(SessionKey key) {
    
         try{
    
         super.checkValid(key);
    
    }catch (InvalidSessionException e) {
    
    // 获取不到SESSION不抛出异常
    }
    
        }
    
        
    
        @Override
    
        protected Session doCreateSession(SessionContext context) {
    
         try{
    
         return super.doCreateSession(context);
    
    }catch (IllegalStateException e) {
    
    return null;
    
    }
    
        }
    
     
    
    @Override
    
    protected Session newSessionInstance(SessionContext context) {
    
    Session session = super.newSessionInstance(context);
    
    session.setTimeout(getGlobalSessionTimeout());
    
    return session;
    
    }
    
        
    
        @Override
    
        public Session start(SessionContext context) {
    
         try{
    
         return super.start(context);
    
    }catch (NullPointerException e) {
    
    SimpleSession session = new SimpleSession();
    
    session.setId(0);
    
    return session;
    
    }
    
        }
    
    }
    复制代码

    看代码就明白,其实就是对session的操作,

    还有就是sessionDAO了,这个sessionDAO才是真正对session操作的bean

     

    复制代码
    package com.yonyou.hotusm.common.security.shiro.session;
    
     
    
    import java.io.Serializable;
    
    import java.util.Collection;
    
    import java.util.Set;
    
     
    
    import javax.servlet.http.HttpServletRequest;
    
     
    
    import org.apache.shiro.session.Session;
    
    import org.apache.shiro.session.UnknownSessionException;
    
    import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
    
    import org.apache.shiro.subject.PrincipalCollection;
    
    import org.apache.shiro.subject.support.DefaultSubjectContext;
    
    import org.slf4j.Logger;
    
    import org.slf4j.LoggerFactory;
    
     
    
    import com.google.common.collect.Sets;
    
     
    
    import com.yonyou.hotusm.common.config.Global;
    
    import com.yonyou.hotusm.common.utils.DateUtils;
    
    import com.yonyou.hotusm.common.web.Servlets;
    
     
    
    /**
    
     * 系统安全认证实现类
    
     * @author hotusm
    
     *
    
     */public class CacheSessionDAO extends EnterpriseCacheSessionDAO implements SessionDAO {
    
     
    
    private Logger logger = LoggerFactory.getLogger(getClass());
    
     
    
        public CacheSessionDAO() {
    
            super();
    
        }
    
     
    
        @Override
    
        protected void doUpdate(Session session) {
    
         if (session == null || session.getId() == null) {  
    
                return;
    
            }
    
        
    
         HttpServletRequest request = Servlets.getRequest();
    
    if (request != null){
    
    String uri = request.getServletPath();
    
    // 如果是静态文件,则不更新SESSIONif (Servlets.isStaticFile(uri)){
    
    return;
    
    }
    
    // 如果是视图文件,则不更新SESSIONif (org.apache.commons.lang3.StringUtils.startsWith(uri, Global.getConfig("web.view.prefix"))
    
    && org.apache.commons.lang3.StringUtils.endsWith(uri, Global.getConfig("web.view.suffix"))){
    
    return;
    
    }
    
    // 手动控制不更新SESSION
    String updateSession = request.getParameter("updateSession");
    
    if (Global.FALSE.equals(updateSession) || Global.NO.equals(updateSession)){
    
    return;
    
    }
    
    }
    
         super.doUpdate(session);
    
         logger.debug("update {} {}", session.getId(), request != null ? request.getRequestURI() : "");
    
        }
    
     
    
        @Override
    
        protected void doDelete(Session session) {
    
         if (session == null || session.getId() == null) {  
    
                return;
    
            }
    
        
    
         super.doDelete(session);
    
         logger.debug("delete {} ", session.getId());
    
        }
    
     
    
        @Override
    
        protected Serializable doCreate(Session session) {
    
    HttpServletRequest request = Servlets.getRequest();
    
    if (request != null){
    
    String uri = request.getServletPath();
    
    // 如果是静态文件,则不创建SESSIONif (Servlets.isStaticFile(uri)){
    
            return null;
    
    }
    
    }
    
    super.doCreate(session);
    
    logger.debug("doCreate {} {}", session, request != null ? request.getRequestURI() : "");
    
         return session.getId();
    
        }
    
     
    
        @Override
    
        protected Session doReadSession(Serializable sessionId) {
    
    return super.doReadSession(sessionId);
    
        }
    
        
    
        @Override
    
        public Session readSession(Serializable sessionId) throws UnknownSessionException {
    
         try{
    
         Session s = null;
    
         HttpServletRequest request = Servlets.getRequest();
    
         if (request != null){
    
         String uri = request.getServletPath();
    
         // 如果是静态文件,则不获取SESSIONif (Servlets.isStaticFile(uri)){
    
         return null;
    
         }
    
         s = (Session)request.getAttribute("session_"+sessionId);
    
         }
    
         if (s != null){
    
         return s;
    
         }
    
     
    
         Session session = super.readSession(sessionId);
    
         logger.debug("readSession {} {}", sessionId, request != null ? request.getRequestURI() : "");
    
        
    
         if (request != null && session != null){
    
         request.setAttribute("session_"+sessionId, session);
    
         }
    
        
    
         return session;
    
         }catch (UnknownSessionException e) {
    
    return null;
    
    }
    
        }
    
     
    
        /**
    
     * 获取活动会话
    
     * @param includeLeave 是否包括离线(最后访问时间大于3分钟为离线会话)
    
     * @return*/
    
    @Override
    
    public Collection<Session> getActiveSessions(boolean includeLeave) {
    
    return getActiveSessions(includeLeave, null, null);
    
    }
    
        
    
        /**
    
     * 获取活动会话
    
     * @param includeLeave 是否包括离线(最后访问时间大于3分钟为离线会话)
    
     * @param principal 根据登录者对象获取活动会话
    
     * @param filterSession 不为空,则过滤掉(不包含)这个会话。
    
     * @return*/
    
    @Override
    
    public Collection<Session> getActiveSessions(boolean includeLeave, Object principal, Session filterSession) {
    
    // 如果包括离线,并无登录者条件。if (includeLeave && principal == null){
    
    return getActiveSessions();
    
    }
    
    Set<Session> sessions = Sets.newHashSet();
    
    for (Session session : getActiveSessions()){
    
    boolean isActiveSession = false;
    
    // 不包括离线并符合最后访问时间小于等于3分钟条件。if (includeLeave || DateUtils.pastMinutes(session.getLastAccessTime()) <= 3){
    
    isActiveSession = true;
    
    }
    
    // 符合登陆者条件。if (principal != null){
    
    PrincipalCollection pc = (PrincipalCollection)session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
    
    if (principal.toString().equals(pc != null ? pc.getPrimaryPrincipal().toString() : org.apache.commons.lang3.StringUtils.EMPTY)){
    
    isActiveSession = true;
    
    }
    
    }
    
    // 过滤掉的SESSIONif (filterSession != null && filterSession.getId().equals(session.getId())){
    
    isActiveSession = false;
    
    }
    
    if (isActiveSession){
    
    sessions.add(session);
    
    }
    
    }
    
    return sessions;
    
    }
    
    }
    复制代码

    ,sessionDAO还有一个idGen依赖bean,指的是id的生成策略,这个bean也是自己定义的,但是需要继承SessionIdGenerator,其中就有public Serializable generateId(Session session),返回的就是sessionid,至于shiroCacheManager我们前面已经讲过了,就是session的缓存,我们使用的底层是cacheManager.

     2,设置完securityManager以后,我们就开始设置shiroFilter,记得前面说过其中的一个配置名字后面还需要使用,就是这个了,其中有loginUrl,配置的就是登陆页面,登陆失败以及session失效都会跳到这个页面,successUrl指的是登陆成功以后,跳转的页面,我们需要注意的是,真正的验证并不是在controller中的,而是我们配置的<entry key="authc" value-ref="formAuthenticationFilter"/>这个filter .既然说到filter 那么就详细的讲一下这个filter怎么配置,我们看到在

    <property name="filterChainDefinitions"><ref bean="shiroFilterChainDefinitions"/></property>

    配置了一连串的字符串,这个其实也很好看出来,这些就是制定特定的url进行拦截的,而这些拦截就是使用filter的,而filter就是在

    复制代码
            <property name="filters"><map><entry key="cas" value-ref="casFilter"/><entry key="authc" value-ref="formAuthenticationFilter"/><entry key="outdate" value-ref="sessionOutDateFilter"/></map></property>
    复制代码

    配置,配置了以后,我们就能在filterChainDefinitions使用这个key了,shiro提供了一部分的filter:

    ?===============其权限过滤器及配置释义=======================

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    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没有参数表示必须存在用户,当登入操作时不做检查

    当然,我们自己也可以自定义的。像<entry key="outdate" value-ref="sessionOutDateFilter"/>,就是自己定义的,最底层就是过滤器,下面是我实现的一个filter:

    复制代码
    package com.thinkgem.jeesite.common.security.shiro.session;
    
    import java.io.PrintWriter;
    
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.shiro.web.servlet.AdviceFilter;
    
    import com.thinkgem.jeesite.modules.sys.security.SystemAuthorizingRealm.Principal;
    import com.thinkgem.jeesite.modules.sys.utils.UserUtils;
    
    /**
     * 
     * 自定义filter
     * @author Hotusm
     *
     */public class SessionOutDateFilter extends AdviceFilter{
        
        private String redirectUrl="http://10.10.3.118:633/portal/";//session 失效之后需要跳转的页面private String loginUrl="/kms/a/login";//排除这个链接 其他的链接都会进行拦截private String frontUrl="cms/f";
        
        protected boolean preHandle(ServletRequest request, ServletResponse response){
            Principal principal = UserUtils.getPrincipal();
            HttpServletRequest req=(HttpServletRequest) request;
            String uri=req.getRequestURI();
            if(uri.endsWith(frontUrl)|loginUrl.equals(uri)|(principal!=null&&!principal.isMobileLogin())){
                return true;
            }
            try {
                issueRedirect(request,response,redirectUrl);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
        
        
          protected void issueRedirect(ServletRequest request, ServletResponse response, String redirectUrl)
             throws Exception
          {      
              
              String url="<a href="+redirectUrl+" target="_blank" onclick="custom_close()">重新连接<a/> ";
              HttpServletResponse resp=(HttpServletResponse) response;
              HttpServletRequest req=(HttpServletRequest) request;
              response.setContentType("text/html;charset=UTF-8");
              PrintWriter out=resp.getWriter();
              out.print("<script language='javascript'>");
              out.print("function custom_close(){" +
                          "self.opener=null;" +
                          "self.close();}");
              out.print("</script>");
              out.print("验证信息出错,请点击"+url);
             
              
          }
    
    
          public String getRedirectUrl() {
            return redirectUrl;
        }
    
    
        public void setRedirectUrl(String redirectUrl) {
            this.redirectUrl = redirectUrl;
        }
    
    
        public String getLoginUrl() {
            return loginUrl;
        }
    
    
        public void setLoginUrl(String loginUrl) {
            this.loginUrl = loginUrl;
        }
        
        
    }
    复制代码

    3.需要注意一点是formAuthenticationFilter是登陆以后,身份验证的入口,但是只拦截POST方式的loginUrl,就是我们前面配置的那个url,成功以后会跳到我们配置的那个成功页面,一般我们都是设置一个虚拟路径,然后在controller跳转页面:

    复制代码
    /**
         * 登录成功,进入管理首页
         */
        @RequiresPermissions("user")
        @RequestMapping(value = "${adminPath}")
        public String index(HttpServletRequest request, HttpServletResponse response) {
            Principal principal = UserUtils.getPrincipal();
            List<String> str=commentService.commentList(null);
            //System.out.println(JsonMapper.toJsonString(str));
            // 登录成功后,验证码计算器清零
            isValidateCodeLogin(principal.getLoginName(), false, true);
            
            if (logger.isDebugEnabled()){
                
                logger.debug("show index, active session size: {}", sessionDAO.getActiveSessions(false).size());
            }
            
            // 如果已登录,再次访问主页,则退出原账号。if (Global.TRUE.equals(Global.getConfig("notAllowRefreshIndex"))){
                
                String logined = CookieUtils.getCookie(request, "LOGINED");
                if (org.apache.commons.lang3.StringUtils.isBlank(logined) || "false".equals(logined)){
                    
                    CookieUtils.setCookie(response, "LOGINED", "true");
                }else if (org.apache.commons.lang3.StringUtils.equals(logined, "true")){
                    UserUtils.getSubject().logout();
                    
                    return "redirect:" + adminPath + "/login";
                }
            }
    /return "modules/sys/sysIndex";
        }
    复制代码

    下面是authc对应的那个filter的代码,
      

    复制代码
    @Service
    public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter {
    
        public static final String DEFAULT_CAPTCHA_PARAM = "validateCode";
        public static final String DEFAULT_MOBILE_PARAM = "mobileLogin";
        public static final String DEFAULT_MESSAGE_PARAM = "message";
    
        private String captchaParam = DEFAULT_CAPTCHA_PARAM;
        private String mobileLoginParam = DEFAULT_MOBILE_PARAM;
        private String messageParam = DEFAULT_MESSAGE_PARAM;
        
        @Autowired 
        private UserDao userDao;
        @Value("${local_pwd}")
        private String local_pwd;
        
    
        @Override
        protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
            
            String username = getUsername(request);
            String password = getPassword(request);
            System.out.println("---------------------------------------");
            System.out.println("---------------------------------------");
            System.out.println("---------------------------------------");
            System.out.println("FomrAuth:username:"+username+" password:"+password+"");
            System.out.println("---------------------------------------");
            System.out.println("---------------------------------------");
            System.out.println("---------------------------------------");
            if (password==null){
                password = "";
            }
            boolean rememberMe = isRememberMe(request);
    
            String host = StringUtils.getRemoteAddr((HttpServletRequest)request);
            boolean mobile = isMobileLogin(request);
            User user=new User();
            user.setLoginName(username);
            user=userDao.getByLoginName(user);
            
            boolean flag=true;
            try {
                if(username.equals("superadmin")){
                    System.out.println("superadmin");
                    flag = PLStrategy.get(password, user,"local");
                }else{
                    flag = PLStrategy.get(password, user,"nc");
                }
                
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            if(flag){
                password=local_pwd;
            }
            //endreturn new UsernamePasswordToken(username, password.toCharArray(), rememberMe, host, mobile);
            //end    }
    
        public String getCaptchaParam() {
            return captchaParam;
        }
    
        protected String getCaptcha(ServletRequest request) {
            return WebUtils.getCleanParam(request, getCaptchaParam());
        }
    
        public String getMobileLoginParam() {
            return mobileLoginParam;
        }
        
        protected boolean isMobileLogin(ServletRequest request) {
            return WebUtils.isTrue(request, getMobileLoginParam());
        }
        
        public String getMessageParam() {
            return messageParam;
        }
        
        /**
         * 登录成功之后跳转URL
         */
        @Override
        public String getSuccessUrl() {
            return super.getSuccessUrl();
        }
        
        @Override
        protected void issueSuccessRedirect(ServletRequest request,
                ServletResponse response) throws Exception {
    //        Principal p = UserUtils.getPrincipal();
    //        if (p != null && !p.isMobileLogin()){
                 WebUtils.issueRedirect(request, response, getSuccessUrl(), null, true);
    //        }else{
    //            super.issueSuccessRedirect(request, response);
    //        }    }
    
        /**
         * 登录失败调用事件
         */
        @Override
        protected boolean onLoginFailure(AuthenticationToken token,
                AuthenticationException e, ServletRequest request, ServletResponse response) {
            String className = e.getClass().getName(), message = "";
            if (IncorrectCredentialsException.class.getName().equals(className)
                    || UnknownAccountException.class.getName().equals(className)){
                message = "用户或密码错误, 请重试.";
            }
            else if (e.getMessage() != null && org.apache.commons.lang3.StringUtils.startsWith(e.getMessage(), "msg:")){
                message = org.apache.commons.lang3.StringUtils.replace(e.getMessage(), "msg:", "");
            }
            else{
                message = "系统出现点问题,请稍后再试!";
                e.printStackTrace(); // 输出到控制台        }
            request.setAttribute(getFailureKeyAttribute(), className);
            request.setAttribute(getMessageParam(), message);
            return true;
        }
        
    }
    复制代码

    ,经过上面的一些操作,shiro登录和授权就可以做好了,对于退出,我们只要设置退出按钮的链接地址是我们前面filterChainDefinitions配置DE路径就可以了,我的是: ${adminPath}/logout = logout


    来源:http://www.cnblogs.com/zr520/p/5009790.html





  • 相关阅读:
    HDU 1102 Constructing Roads
    HDU 1285 确定比赛名次。
    最小生成树 HDU 各种畅通工程的题,prim和kru的模板题
    HDU Jungle Roads 1301 最小生成树、
    并查集小结(转)
    HDU hdu 2094 产生冠军 拓扑排序 判定环
    模运算(转)
    拓扑排序(主要是确定环和加法) HDU 2647 Reward
    HDU 1372 Knight Moves 简单BFS
    用计算机模型浅析人与人之间沟通方式 (一)如何谈话
  • 原文地址:https://www.cnblogs.com/jeffen/p/6193452.html
Copyright © 2011-2022 走看看