Shrio框架Token认证思路

默认Shiro Session认证方式解析

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

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

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<String, byte[]> sessionRedis;
    private int sessionTimeoutInSec = 4 * 8600;

    public RedisTemplate<String, byte[]> getSessionRedis() {
        return sessionRedis;
    }

    public LJNativeSessionManager(RedisTemplate<String, byte[]> sessionRedis) {
        this.sessionRedis = sessionRedis;
    }

    public void setSessionRedis(RedisTemplate<String, byte[]> sessionRedis) {
        this.sessionRedis = sessionRedis;
    }

    /**
     * Template method that allows subclasses to react to a new session being created.
     * <p/>
     * This method is invoked <em>before</em> 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).
     * <p/>
     * 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;
    }
}
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值