Shrio框架Token认证思路

默认Shiro Session认证方式解析

shiro session的获取最核心的类是 DefaultWebSessionManager

默认的sesionId是分两个步骤的,第一步是从Cookie中获取,Cookie没有的的话是从Url Path中获取

Shrio框架Token认证思路_第1张图片Shiro Session Token认证方式

既然Shrio是从Cookie和Url中获取的SessionID,那么不适合Native手机的开发,一般会和Native开发人员定义一个Token协议,可以把Token放在Http header中,放Token有两种思路,第一种直接放入sessionId,第二种自定义Token生成然后Token和sessionId绑定.我更倾向于第二种方案. 继承类DefaultSessionManager实现自己的session获取方法.代码如下

package com.test.plats.uc.passport.client.support.session.mgt;
import org.apache.shiro.session.ExpiredSessionException;
import org.apache.shiro.session.InvalidSessionException;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.session.mgt.SessionContext;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.WebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;

/**
 * @author mengxr
 * @Description: Native SessionManager
 * @date 2018/5/31 下午4:00
 */
public class LJNativeSessionManager extends DefaultSessionManager implements WebSessionManager {

    private static final Logger log = LoggerFactory.getLogger(LJNativeSessionManager.class);
    private static final String DEFAULT_TOKEN_KEY = "token_";

    public static final String accessToken = "Access-Token";
    private RedisTemplate sessionRedis;
    private int sessionTimeoutInSec = 4 * 8600;

    public RedisTemplate getSessionRedis() {
        return sessionRedis;
    }

    public LJNativeSessionManager(RedisTemplate sessionRedis) {
        this.sessionRedis = sessionRedis;
    }

    public void setSessionRedis(RedisTemplate sessionRedis) {
        this.sessionRedis = sessionRedis;
    }

    /**
     * Template method that allows subclasses to react to a new session being created.
     * 

* This method is invoked before any session listeners are notified. * * @param session the session that was just {@link #createSession created}. * @param context the {@link SessionContext SessionContext} that was used to start the session. */ @Override protected void onStart(Session session, SessionContext context) { super.onStart(session, context); if (!WebUtils.isHttp(context)) { log.debug("SessionContext argument is not HTTP compatible or does not have an HTTP request " + "pair. No session ID Access-Token will be set."); return; } HttpServletRequest request = WebUtils.getHttpRequest(context); String accessToken = getAccessToken(request); if (null != session && null != accessToken) { String key = DEFAULT_TOKEN_KEY + accessToken; String id = session.getId().toString(); byte[] bytes = sessionRedis.opsForValue().get(key); if (id != null && null == bytes || bytes.length == 0) { byte[] content = SerializeUtils.serialize(id); sessionRedis.opsForValue().set(key, content, sessionTimeoutInSec, TimeUnit.SECONDS); } if (id != null) { request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id); //automatically mark it valid here. If it is invalid, the //onUnknownSession method below will be invoked and we'll remove the attribute at that time. request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); } } } /** * get access token * * @param request * @return */ private String getAccessToken(HttpServletRequest request) { String accessToken = request.getHeader(accessToken); return accessToken != null ? accessToken : ""; } @Override protected Serializable getSessionId(SessionKey sessionKey) { Serializable id = super.getSessionId(sessionKey); if (null == id && WebUtils.isWeb(sessionKey)) { HttpServletRequest request = WebUtils.getHttpRequest(sessionKey); String accessToken = getAccessToken(request); if (null != accessToken && accessToken.length() > 0) { byte[] bytes = sessionRedis.opsForValue().get(DEFAULT_TOKEN_KEY + accessToken); id = (Serializable) SerializeUtils.deserialize(bytes); } } return id; } @Override protected void onExpiration(Session s, ExpiredSessionException ese, SessionKey key) { super.onExpiration(s, ese, key); onInvalidation(key); } @Override protected void onInvalidation(Session s, InvalidSessionException ise, SessionKey key) { super.onInvalidation(s, ise, key); onInvalidation(key); } private void onInvalidation(SessionKey key) { ServletRequest request = WebUtils.getRequest(key); if (request != null) { request.removeAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID); } if (WebUtils.isHttp(key)) { log.debug("Referenced session was invalid. Removing session ID Access-Token."); HttpServletRequest httpRequest = WebUtils.getHttpRequest(key); String accessToken = getAccessToken(httpRequest); if (null != accessToken && accessToken.length() > 0) { sessionRedis.delete(DEFAULT_TOKEN_KEY + accessToken); } } else { log.debug("SessionKey argument is not HTTP compatible or does not have an HTTP request/response " + "pair. Session ID Access-Token will not be removed due to invalidated session."); } } @Override protected void onStop(Session session, SessionKey key) { super.onStop(session, key); if (WebUtils.isHttp(key)) { HttpServletRequest request = WebUtils.getHttpRequest(key); log.debug("Session has been stopped (subject logout or explicit stop). Removing session ID Access-Token."); String accessToken = getAccessToken(request); if (null != accessToken && accessToken.length() > 0) { sessionRedis.delete(DEFAULT_TOKEN_KEY + accessToken); } } else { log.debug("SessionKey argument is not HTTP compatible or does not have an HTTP request/response " + "pair. Session ID Access-Token will not be removed due to stopped session."); } } /** * Returns {@code true} if session management and storage is managed by the underlying Servlet container or * {@code false} if managed by Shiro directly (called 'native' sessions). *

* If sessions are enabled, Shiro can make use of Sessions to retain security information from * request to request. This method indicates whether Shiro would use the Servlet container sessions to fulfill its * needs, or if it would use its own native session management instead (which can support enterprise features * - like distributed caching - in a container-independent manner). * * @return {@code true} if session management and storage is managed by the underlying Servlet container or * {@code false} if managed by Shiro directly (called 'native' sessions). */ @Override public boolean isServletContainerSessions() { return false; } }

你可能感兴趣的:(Micro,service,J2ee)