Shiro實現(xiàn)后臺和前端共用

1.近期在弄shiro的安全框架忧便,目前情況是后端使用form請求進行登錄徒役,前端使用ajax進行登錄寞蚌。一是為減少重復代碼編寫,二是為了深入了解shiro的一些知識麸折。

2.后端框架使用ssm + shiro1.2.3锡凝,緩存方面主要用ehcache2.6.11或者redis,主要的shiro pom依賴有如下:

 <properties>
            <shiro.version>1.2.3</shiro.version>
 </properties>
<dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-cas</artifactId>
            <version>${shiro.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>${shiro.version}</version>
        </dependency>

3.spring配置shiro

<!-- Shiro權(quán)限過濾過濾器定義 -->
    <bean name="shiroFilterChainDefinitions" class="java.lang.String">
        <constructor-arg>
            <value>
                /open/smartFactory/** = authc
                /weixin/** = anon
                /static/** = anon
                /userfiles/** = anon
                ${adminPath}/cas = cas
                ${adminPath}/login = authc
                ${adminPath}/logout = logout
                ${adminPath}/** = user
                /act/editor/** = user
                /ReportServer/** = user
            </value>
        </constructor-arg>
    </bean>
    
    <!-- 安全認證過濾器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value="${adminPath}/login" />
        <property name="successUrl" value="${adminPath}?login" />
        <property name="filters">
            <map>
                <entry key="cas" value-ref="casFilter"/>
                <entry key="authc" value-ref="formAuthenticationFilter"/>
            </map>
        </property>
        <property name="filterChainDefinitions">
            <ref bean="shiroFilterChainDefinitions"/>
        </property>
    </bean>
    
    <!-- CAS認證過濾器 -->  
    <bean id="casFilter" class="org.apache.shiro.cas.CasFilter">  
        <property name="failureUrl" value="${adminPath}/login"/>
    </bean>
    
    <!-- 定義Shiro安全管理配置 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="systemAuthorizingRealm" />
        <property name="sessionManager" ref="sessionManager" />
        <property name="cacheManager" ref="shiroCacheManager" />
    </bean>
    
    <!-- 自定義會話管理配置 -->
    <bean id="sessionManager" class="com.thinkgem.jeesite.common.security.shiro.session.SessionManager"> 
        <property name="sessionDAO" ref="sessionDAO"/>
        
        <!-- 會話超時時間垢啼,單位:毫秒  -->
        <property name="globalSessionTimeout" value="${session.sessionTimeout}"/>
        
        <!-- 定時清理失效會話, 清理用戶直接關(guān)閉瀏覽器造成的孤立會話   -->
        <property name="sessionValidationInterval" value="${session.sessionTimeoutClean}"/>
<!--        <property name="sessionValidationSchedulerEnabled" value="false"/> -->
        <property name="sessionValidationSchedulerEnabled" value="true"/>
        
        <property name="sessionIdCookie" ref="sessionIdCookie"/>
        <property name="sessionIdCookieEnabled" value="true"/>
    </bean>
    
    <!-- 指定本系統(tǒng)SESSIONID, 默認為: JSESSIONID 問題: 與SERVLET容器名沖突, 如JETTY, TOMCAT 等默認JSESSIONID,
        當跳出SHIRO SERVLET時如ERROR-PAGE容器會為JSESSIONID重新分配值導致登錄會話丟失! -->
    <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
        <constructor-arg name="name" value="jeesite.session.id"/>
    </bean>

    <!-- 自定義Session存儲容器 -->
<!--    <bean id="sessionDAO" class="com.thinkgem.jeesite.common.security.shiro.session.JedisSessionDAO"> -->
<!--        <property name="sessionIdGenerator" ref="idGen" /> -->
<!--        <property name="sessionKeyPrefix" value="${redis.keyPrefix}_session_" /> -->
<!--    </bean> -->
    <bean id="sessionDAO" class="com.thinkgem.jeesite.common.security.shiro.session.CacheSessionDAO">
        <property name="sessionIdGenerator" ref="idGen" />
        <property name="activeSessionsCacheName" value="activeSessionsCache" />
        <property name="cacheManager" ref="shiroCacheManager" />
    </bean>
    
    <!-- 自定義系統(tǒng)緩存管理器-->
<!--    <bean id="shiroCacheManager" class="com.thinkgem.jeesite.common.security.shiro.cache.JedisCacheManager"> -->
<!--        <property name="cacheKeyPrefix" value="${redis.keyPrefix}_cache_" /> -->
<!--    </bean> -->
    <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManager" ref="cacheManager"/>
    </bean>
    
    <!-- 保證實現(xiàn)了Shiro內(nèi)部lifecycle函數(shù)的bean執(zhí)行 -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    
    <!-- AOP式方法級權(quán)限檢查  -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
        <property name="proxyTargetClass" value="true" />
    </bean>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>

4.shiro的登錄

@Service
public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter {

    private static final Logger LOGGER = LoggerFactory.getLogger(FormAuthenticationFilter.class);

    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;

    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
        String username = getUsername(request);
        String password = getPassword(request);
        if (password == null) {
            password = "";
        }
        boolean rememberMe = isRememberMe(request);
        String host = StringUtils.getRemoteAddr((HttpServletRequest) request);
        String captcha = getCaptcha(request);
        boolean mobile = isMobileLogin(request);
        return new UsernamePasswordToken(username, password.toCharArray(), rememberMe, host, captcha, mobile);
    }

    /**
     * 每次請求的時候都先進入此方法
     *
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request,
                                     ServletResponse response) throws Exception {
        HttpServletResponse resp = (HttpServletResponse) response;
        HttpServletRequest resq = (HttpServletRequest) request;
        // 后臺form
        if (isLoginRequest(request, response)) {
            if (isLoginSubmission(request, response)) {
                return executeLogin(request, response);
            } else {
                return true;
            }
        } else {
            // ajax請求
            if ("XMLHttpRequest"
                    .equalsIgnoreCase(((HttpServletRequest) request)
                            .getHeader("X-Requested-With"))) {
                saveRequestAndRedirectToLogin(request, response);
            } else {
                request.setCharacterEncoding("utf-8");
                resp.setHeader("Content-type", "text/html;charset=UTF-8");
                resp.setCharacterEncoding("utf-8");

                Result result = ResultGenerator.getNotLoginResult(
                        new String("沒有登錄,請登錄".getBytes(), "UTF-8"));
                resp.getWriter().write(JSON.toJSONString(result));
                resp.getWriter().flush();
                resp.getWriter().close();
            }
            return false;
        }
    }

    /**
     * 獲取登錄用戶名
     */

    protected String getUsername(ServletRequest request, ServletResponse response) {
        String username = super.getUsername(request);
        if (StringUtils.isBlank(username)) {
            username = StringUtils.toString(request.getAttribute(getUsernameParam()), StringUtils.EMPTY);
        }
        return username;
    }

    /**
     * 獲取登錄密碼
     */
    @Override
    protected String getPassword(ServletRequest request) {
        String password = super.getPassword(request);
        if (StringUtils.isBlank(password)) {
            password = StringUtils.toString(request.getAttribute(getPasswordParam()), StringUtils.EMPTY);
        }
        return password;
    }

    /**
     * 獲取記住我
     */
    @Override
    protected boolean isRememberMe(ServletRequest request) {
        String isRememberMe = WebUtils.getCleanParam(request, getRememberMeParam());
        if (StringUtils.isBlank(isRememberMe)) {
            isRememberMe = StringUtils.toString(request.getAttribute(getRememberMeParam()), StringUtils.EMPTY);
        }
        return StringUtils.toBoolean(isRememberMe);
    }

    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;
    }

    /**
     * 登錄成功之后跳轉(zhuǎn)URL
     */
    public String getSuccessUrl() {
        return super.getSuccessUrl();
    }

    @Override
    protected void issueSuccessRedirect(ServletRequest request,
                                        ServletResponse response) throws Exception {
        SystemAuthorizingRealm.Principal p = UserUtils.getPrincipal();
        if (p != null && !p.isMobileLogin()) {
            WebUtils.issueRedirect(request, response, getSuccessUrl(), null, true);
        }
//        else {
//            super.issueSuccessRedirect(request, response);
//        }
    }


    /**
     * 登錄成功回調(diào)
     *
     * @param token
     * @param subject
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest resq = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        // 返回ajax數(shù)據(jù)
        if (!isBackground(request)) {
            request.setCharacterEncoding("utf-8");
            resp.setHeader("Content-type", "text/html;charset=UTF-8");
            resp.setCharacterEncoding("utf-8");
            String tokenId = (String) UserUtils.getSession().getId();
            Map tokenMap = Maps.newHashMapWithExpectedSize(1);
            tokenMap.put(Global.TOKEN, tokenId);
            Result result = ResultGenerator.getSuccessResult(tokenMap);
            response.getWriter().write(JSON.toJSONString(result));
            response.getWriter().flush();
            response.getWriter().close();
        } else {
            // modelandview方式
            issueSuccessRedirect(request, response);
        }
        return false;
    }

    /**
     * 登錄失敗調(diào)用事件
     */
    @Override
    protected boolean onLoginFailure(AuthenticationToken token,
                                     AuthenticationException e, ServletRequest request, ServletResponse response) {
        HttpServletResponse resp = (HttpServletResponse) response;
        String className = e.getClass().getName(), message = "";

        // 返回json格式信息

        if (IncorrectCredentialsException.class.getName().equals(className)
                || UnknownAccountException.class.getName().equals(className)) {
            message = "用戶或密碼錯誤, 請重試.";
        } else if (e.getMessage() != null && StringUtils.startsWith(e.getMessage(), "msg:")) {
            message = StringUtils.replace(e.getMessage(), "msg:", "");
        } else {
            message = "系統(tǒng)出現(xiàn)點問題窜锯,請稍后再試!";
            e.printStackTrace(); // 輸出到控制臺
        }
        Result result = ResultGenerator.getFailResult(message);

        // 返回ajax信息
        if (!isBackground(request)) {
            try {
                request.setCharacterEncoding("utf-8");
                resp.setHeader("Content-type", "text/html;charset=UTF-8");
                resp.setCharacterEncoding("utf-8");
                response.getWriter().write(JSON.toJSONString(result));
                response.getWriter().flush();
                response.getWriter().close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        } else {
            request.setAttribute(getFailureKeyAttribute(), className);
            request.setAttribute(getMessageParam(), message);
        }
        return true;
    }


    private boolean isBackground(ServletRequest request) {
        if ("XMLHttpRequest"
                .equalsIgnoreCase(((HttpServletRequest) request)
                        .getHeader("X-Requested-With"))) {
            return true;
        }
        return false;
    }
}

5.系統(tǒng)的認證實現(xiàn)類

@Service
//@DependsOn({"userDao","roleDao","menuDao"})
public class SystemAuthorizingRealm extends AuthorizingRealm {

    private Logger logger = LoggerFactory.getLogger(getClass());
    
    private SystemService systemService;
    
    public SystemAuthorizingRealm() {
        this.setCachingEnabled(false);
    }

    @Override
    protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
        super.assertCredentialsMatch(token, info);
    }

    /**
     * 認證回調(diào)函數(shù), 登錄時調(diào)用
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) {
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        
        int activeSessionSize = getSystemService().getSessionDao().getActiveSessions(false).size();
        if (logger.isDebugEnabled()){
            logger.debug("login submit, active session size: {}, username: {}", activeSessionSize, token.getUsername());
        }
        
        // 校驗登錄驗證碼
        if (LoginController.isValidateCodeLogin(token.getUsername(), false, false)){
            Session session = UserUtils.getSession();
            String code = (String)session.getAttribute(ValidateCodeServlet.VALIDATE_CODE);
            if (token.getCaptcha() == null || !token.getCaptcha().toUpperCase().equals(code)){
                throw new AuthenticationException("msg:驗證碼錯誤, 請重試.");
            }
        }
        
        // 校驗用戶名密碼
        User user = getSystemService().getUserByLoginName(token.getUsername());
        if (user != null) {
            if (Global.NO.equals(user.getLoginFlag())){
                throw new AuthenticationException("msg:該已帳號禁止登錄.");
            }
            byte[] salt = Encodes.decodeHex(user.getPassword().substring(0,16));
            return new SimpleAuthenticationInfo(new Principal(user, token.isMobileLogin()), 
                    user.getPassword().substring(16), ByteSource.Util.bytes(salt), getName());
        } else {
            return null;
        }
    }
    
    /**
     * 獲取權(quán)限授權(quán)信息芭析,如果緩存中存在锚扎,則直接從緩存中獲取,否則就重新獲取馁启, 登錄成功后調(diào)用
     */
    protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {
        if (principals == null) {
            return null;
        }
        
        AuthorizationInfo info = null;

        info = (AuthorizationInfo)UserUtils.getCache(UserUtils.CACHE_AUTH_INFO);

        if (info == null) {
            info = doGetAuthorizationInfo(principals);
            if (info != null) {
                UserUtils.putCache(UserUtils.CACHE_AUTH_INFO, info);
            }
        }

        return info;
    }

    /**
     * 授權(quán)查詢回調(diào)函數(shù), 進行鑒權(quán)但緩存中無用戶的授權(quán)信息時調(diào)用
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        Principal principal = (Principal) getAvailablePrincipal(principals);
        // 獲取當前已登錄的用戶
        if (!Global.TRUE.equals(Global.getConfig("user.multiAccountLogin"))){
            Collection<Session> sessions = getSystemService().getSessionDao().getActiveSessions(true, principal, UserUtils.getSession());
            if (sessions.size() > 0){
                // 如果是登錄進來的驾孔,則踢出已在線用戶
                if (UserUtils.getSubject().isAuthenticated()){
                    for (Session session : sessions){
                        getSystemService().getSessionDao().delete(session);
                    }
                }
                // 記住我進來的,并且當前用戶已登錄惯疙,則退出當前用戶提示信息翠勉。
                else{
                    UserUtils.getSubject().logout();
                    throw new AuthenticationException("msg:賬號已在其它地方登錄,請重新登錄霉颠。");
                }
            }
        }
        User user = getSystemService().getUserByLoginName(principal.getLoginName());
        if (user != null) {
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            List<Menu> list = UserUtils.getMenuList();
            for (Menu menu : list){
                if (StringUtils.isNotBlank(menu.getPermission())){
                    // 添加基于Permission的權(quán)限信息
                    for (String permission : StringUtils.split(menu.getPermission(),",")){
                        info.addStringPermission(permission);
                    }
                }
            }
            // 添加用戶權(quán)限
            info.addStringPermission("user");
            // 添加用戶角色信息
            for (Role role : user.getRoleList()){
                info.addRole(role.getEnname());
            }
            // 更新登錄IP和時間
            getSystemService().updateUserLoginInfo(user);
            // 記錄登錄日志
            LogUtils.saveLog(Servlets.getRequest(), "系統(tǒng)登錄");
            return info;
        } else {
            return null;
        }
    }
    
    @Override
    protected void checkPermission(Permission permission, AuthorizationInfo info) {
        authorizationValidate(permission);
        super.checkPermission(permission, info);
    }
    
    @Override
    protected boolean[] isPermitted(List<Permission> permissions, AuthorizationInfo info) {
        if (permissions != null && !permissions.isEmpty()) {
            for (Permission permission : permissions) {
                authorizationValidate(permission);
            }
        }
        return super.isPermitted(permissions, info);
    }
    
    @Override
    public boolean isPermitted(PrincipalCollection principals, Permission permission) {
        authorizationValidate(permission);
        return super.isPermitted(principals, permission);
    }
    
    @Override
    protected boolean isPermittedAll(Collection<Permission> permissions, AuthorizationInfo info) {
        if (permissions != null && !permissions.isEmpty()) {
            for (Permission permission : permissions) {
                authorizationValidate(permission);
            }
        }
        return super.isPermittedAll(permissions, info);
    }
    
    /**
     * 授權(quán)驗證方法
     * @param permission
     */
    private void authorizationValidate(Permission permission){
        // 模塊授權(quán)預留接口
    }
    
    /**
     * 設(shè)定密碼校驗的Hash算法與迭代次數(shù)
     */
    @PostConstruct
    public void initCredentialsMatcher() {
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(SystemService.HASH_ALGORITHM);
        matcher.setHashIterations(SystemService.HASH_INTERATIONS);
        setCredentialsMatcher(matcher);
    }
    
//  /**
//   * 清空用戶關(guān)聯(lián)權(quán)限認證对碌,待下次使用時重新加載
//   */
//  public void clearCachedAuthorizationInfo(Principal principal) {
//      SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName());
//      clearCachedAuthorizationInfo(principals);
//  }

    /**
     * 清空所有關(guān)聯(lián)認證
     * @Deprecated 不需要清空,授權(quán)緩存保存到session中
     */
    @Deprecated
    public void clearAllCachedAuthorizationInfo() {
//      Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
//      if (cache != null) {
//          for (Object key : cache.keys()) {
//              cache.remove(key);
//          }
//      }
    }

    /**
     * 獲取系統(tǒng)業(yè)務對象
     */
    public SystemService getSystemService() {
        if (systemService == null){
            systemService = SpringContextHolder.getBean(SystemService.class);
        }
        return systemService;
    }
    
    /**
     * 授權(quán)用戶信息
     */
    public static class Principal implements Serializable {

        private static final long serialVersionUID = 1L;
        
        private String id; // 編號
        private String loginName; // 登錄名
        private String name; // 姓名
        private boolean mobileLogin; // 是否手機登錄
        
//      private Map<String, Object> cacheMap;

        public Principal(User user, boolean mobileLogin) {
            this.id = user.getId();
            this.loginName = user.getLoginName();
            this.name = user.getName();
            this.mobileLogin = mobileLogin;
        }

        public String getId() {
            return id;
        }

        public String getLoginName() {
            return loginName;
        }

        public String getName() {
            return name;
        }

        public boolean isMobileLogin() {
            return mobileLogin;
        }

//      @JsonIgnore
//      public Map<String, Object> getCacheMap() {
//          if (cacheMap==null){
//              cacheMap = new HashMap<String, Object>();
//          }
//          return cacheMap;
//      }

        /**
         * 獲取SESSIONID
         */
        public String getSessionid() {
            try{
                return (String) UserUtils.getSession().getId();
            }catch (Exception e) {
                return "";
            }
        }
        
        @Override
        public String toString() {
            return id;
        }

    }
}

6.UsernamePasswordToken

public class UsernamePasswordToken extends org.apache.shiro.authc.UsernamePasswordToken {

    private static final long serialVersionUID = 1L;

    private String captcha;
    private boolean mobileLogin;
    
    public UsernamePasswordToken() {
        super();
    }

    public UsernamePasswordToken(String username, char[] password,
            boolean rememberMe, String host, String captcha, boolean mobileLogin) {
        super(username, password, rememberMe, host);
        this.captcha = captcha;
        this.mobileLogin = mobileLogin;
    }

    public String getCaptcha() {
        return captcha;
    }

    public void setCaptcha(String captcha) {
        this.captcha = captcha;
    }

    public boolean isMobileLogin() {
        return mobileLogin;
    }
    
}

7.sessionDao

public interface SessionDAO extends org.apache.shiro.session.mgt.eis.SessionDAO {

    /**
     * 獲取活動會話
     * @param includeLeave 是否包括離線(最后訪問時間大于3分鐘為離線會話)
     * @return
     */
    public Collection<Session> getActiveSessions(boolean includeLeave);
    
    /**
     * 獲取活動會話
     * @param includeLeave 是否包括離線(最后訪問時間大于3分鐘為離線會話)
     * @param principal 根據(jù)登錄者對象獲取活動會話
     * @param filterSession 不為空掉分,則過濾掉(不包含)這個會話俭缓。
     * @return
     */
    public Collection<Session> getActiveSessions(boolean includeLeave, Object principal, Session filterSession);
    
}

7.自定義web會話管理類

public class SessionManager extends DefaultWebSessionManager {

    private static final Logger LOGGER = LoggerFactory.getLogger(SessionManager.class);

    public SessionManager() {
        super();
    }

    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        LOGGER.info("根據(jù)token獲取sessionID,確定用戶是否登錄");

        // 如果參數(shù)中包含“__sid”參數(shù)酥郭,則使用此sid會話华坦。 例如:http://localhost/project?__sid=xxx&__cookie=true
        String sid = request.getParameter("__sid");
        if (StringUtils.isNotBlank(sid)) {
            // 是否將sid保存到cookie,瀏覽器模式下使用此參數(shù)不从。
            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);
            }
            // 設(shè)置當前session狀態(tài)
            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;
        }
    }

    public Date getStartTimestamp(SessionKey key) {
        try {
            return super.getStartTimestamp(key);
        } catch (InvalidSessionException e) {
            // 獲取不到SESSION不拋出異常
            return null;
        }
    }

    public Date getLastAccessTime(SessionKey key) {
        try {
            return super.getLastAccessTime(key);
        } catch (InvalidSessionException e) {
            // 獲取不到SESSION不拋出異常
            return null;
        }
    }

    public long getTimeout(SessionKey key) {
        try {
            return super.getTimeout(key);
        } catch (InvalidSessionException e) {
            // 獲取不到SESSION不拋出異常
            return 0;
        }
    }

    public void setTimeout(SessionKey key, long maxIdleTimeInMillis) {
        try {
            super.setTimeout(key, maxIdleTimeInMillis);
        } catch (InvalidSessionException e) {
            // 獲取不到SESSION不拋出異常
        }
    }

    public void touch(SessionKey key) {
        try {
            super.touch(key);
        } catch (InvalidSessionException e) {
            // 獲取不到SESSION不拋出異常
        }
    }

    public String getHost(SessionKey key) {
        try {
            return super.getHost(key);
        } catch (InvalidSessionException e) {
            // 獲取不到SESSION不拋出異常
            return null;
        }
    }

    public Collection<Object> getAttributeKeys(SessionKey key) {
        try {
            return super.getAttributeKeys(key);
        } catch (InvalidSessionException e) {
            // 獲取不到SESSION不拋出異常
            return null;
        }
    }

    public Object getAttribute(SessionKey sessionKey, Object attributeKey) {
        try {
            return super.getAttribute(sessionKey, attributeKey);
        } catch (InvalidSessionException e) {
            // 獲取不到SESSION不拋出異常
            return null;
        }
    }

    public void setAttribute(SessionKey sessionKey, Object attributeKey, Object value) {
        try {
            super.setAttribute(sessionKey, attributeKey, value);
        } catch (InvalidSessionException e) {
            // 獲取不到SESSION不拋出異常
        }
    }

    public Object removeAttribute(SessionKey sessionKey, Object attributeKey) {
        try {
            return super.removeAttribute(sessionKey, attributeKey);
        } catch (InvalidSessionException e) {
            // 獲取不到SESSION不拋出異常
            return null;
        }
    }

    public void stop(SessionKey key) {
        try {
            super.stop(key);
        } catch (InvalidSessionException e) {
            // 獲取不到SESSION不拋出異常
        }
    }

    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;
        }
    }
}

8.session緩存dao

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();
            // 如果是靜態(tài)文件惜姐,則不更新SESSION
            if (Servlets.isStaticFile(uri)){
                return;
            }
            // 如果是視圖文件,則不更新SESSION
            if (StringUtils.startsWith(uri, Global.getConfig("web.view.prefix"))
                    && 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();
            // 如果是靜態(tài)文件椿息,則不創(chuàng)建SESSION
            if (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();
                // 如果是靜態(tài)文件歹袁,則不獲取SESSION
                if (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 根據(jù)登錄者對象獲取活動會話
     * @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() : StringUtils.EMPTY)){
                    isActiveSession = true;
                }
            }
            // 過濾掉的SESSION
            if (filterSession != null && filterSession.getId().equals(session.getId())){
                isActiveSession = false;
            }
            if (isActiveSession){
                sessions.add(session);
            }
        }
        return sessions;
    }
    
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末迁杨,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子凄硼,更是在濱河造成了極大的恐慌铅协,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,946評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件摊沉,死亡現(xiàn)場離奇詭異狐史,居然都是意外死亡,警方通過查閱死者的電腦和手機说墨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,336評論 3 399
  • 文/潘曉璐 我一進店門骏全,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人婉刀,你說我怎么就攤上這事吟温。” “怎么了突颊?”我有些...
    開封第一講書人閱讀 169,716評論 0 364
  • 文/不壞的土叔 我叫張陵鲁豪,是天一觀的道長。 經(jīng)常有香客問我律秃,道長爬橡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,222評論 1 300
  • 正文 為了忘掉前任棒动,我火速辦了婚禮糙申,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘船惨。我一直安慰自己柜裸,他們只是感情好,可當我...
    茶點故事閱讀 69,223評論 6 398
  • 文/花漫 我一把揭開白布粱锐。 她就那樣靜靜地躺著疙挺,像睡著了一般。 火紅的嫁衣襯著肌膚如雪怜浅。 梳的紋絲不亂的頭發(fā)上铐然,一...
    開封第一講書人閱讀 52,807評論 1 314
  • 那天,我揣著相機與錄音恶座,去河邊找鬼搀暑。 笑死,一個胖子當著我的面吹牛跨琳,可吹牛的內(nèi)容都是我干的自点。 我是一名探鬼主播,決...
    沈念sama閱讀 41,235評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼脉让,長吁一口氣:“原來是場噩夢啊……” “哼桂敛!你這毒婦竟也來了冈绊?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,189評論 0 277
  • 序言:老撾萬榮一對情侶失蹤埠啃,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后伟恶,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體碴开,經(jīng)...
    沈念sama閱讀 46,712評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,775評論 3 343
  • 正文 我和宋清朗相戀三年博秫,在試婚紗的時候發(fā)現(xiàn)自己被綠了潦牛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,926評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡挡育,死狀恐怖巴碗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情即寒,我是刑警寧澤橡淆,帶...
    沈念sama閱讀 36,580評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站母赵,受9級特大地震影響逸爵,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜凹嘲,卻給世界環(huán)境...
    茶點故事閱讀 42,259評論 3 336
  • 文/蒙蒙 一师倔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧周蹭,春花似錦趋艘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,750評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至俱尼,卻和暖如春抖单,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背遇八。 一陣腳步聲響...
    開封第一講書人閱讀 33,867評論 1 274
  • 我被黑心中介騙來泰國打工矛绘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人刃永。 一個月前我還...
    沈念sama閱讀 49,368評論 3 379
  • 正文 我出身青樓货矮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親斯够。 傳聞我的和親對象是個殘疾皇子囚玫,可洞房花燭夜當晚...
    茶點故事閱讀 45,930評論 2 361

推薦閱讀更多精彩內(nèi)容