Shiro重构:整合token和cookie实现登陆及验证

本文详细介绍了如何在Shiro框架中重构认证服务,以支持移动端(小程序、H5、App)的token认证,同时保留对PC端cookie的支持。重点在于重写DefaultWebSessionManager,处理不同设备间的会话管理并确保登录验证的灵活性。

Shiro重构:整合token和cookie实现登陆及验证

认证服务开始只支持PC端的cookie认证方式,因业务需要,要对小程序、H5、App等移动端设备进行认证,这里复用认证服务。由于小程序不支持cookie认证方式,采用token认证方式,这里对认证服务进行重构。认证服务主要是有Shiro框架研发,我们简单熟悉下Cookie的认证流程。

Shiro的cookie认证流程我们简单说明下,

  • shiro是在其默认的会话管理器DefaultWebSessionManager中获取请求携带过来的cookie。主要的功能是添加、删除SessionId到Cookie、读取Cookie获得SessionId。

用户首先通过用户名口令或手机号+验证码方式,调用认证服务接口进行登陆操作。

Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
subject.login(token);//当调用subject的登入方法时,会跳转到认证的方法上
User user = (User) subject.getPrincipal();//获取当前登陆对象
//TODO: 做之后的事情

这里用户登陆成功后,会将通过DefaultWebSessionManager,生成SessionId,并将sid做为key保存在redis缓存中(通过redis做session共享)。并将sid保存到cookie中,返回浏览器。

  • 浏览器发起请求,传递cookie,Shiro框架通过DefaultWebSessionManager中的getSessionId方法,获取cookie中的Sid.
public Serializable getSessionId(SessionKey key) {
        Serializable id = super.getSessionId(key);
        if (id == null && WebUtils.isWeb(key)) {
            ServletRequest request = WebUtils.getRequest(key);
            ServletResponse response = WebUtils.getResponse(key);
            id = this.getSessionId(request, response);
        }
        return id;
}

获取到sid后,到redis中查询用户信息,如果能获取到则通过,否则进行拦截处理。

一、重写DefaultWebSessionManager方法

用户请求到服务端后,首先判断该请求是否需要鉴权,如果不需要鉴权则直接放行。如果需要鉴权,则进入DefaultWebSessionManager中的getSessionId方法。这里我们重新定义个SessionManager,重写getSessionId方法。

我们定义个CustomerWebSessionManager继承DefaultWebSessionManager,重写getSessionId方法,我们首先从请求头中判断是否存在token,如果存在我们直接获取请求头中的token信息,并将token设置到请求头中request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, token)

public class CustomerWebSessionManager extends DefaultWebSessionManager {
    private static final String AUTH_TOKEN = "token";
    private static final String DEVICE = "device";
    private static final String MOBILE = "mobile";

    public CustomerWebSessionManager() {
        super();
    }

    /**
     * 重写父类获取sessionID的方法,若请求为APP或者H5则从请求头中取出token
     *
     * @param request  请求参数
     * @param response 响应参数
     * @return id
     */
    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        if (!(request instanceof HttpServletRequest)) {
            log.debug("Current request is not an HttpServletRequest - cannot get session ID.  Returning null.");
            return null;
        }
        HttpServletRequest httpRequest = WebUtils.toHttp(request);
        if (StringUtils.hasText(httpRequest.getHeader(AUTH_TOKEN))) {
            //从header中获取token
            String token = httpRequest.getHeader(AUTH_TOKEN);
            // 每次读取之后都把当前的token放入response中
            HttpServletResponse httpResponse = WebUtils.toHttp(response);
            if (StringUtils.hasText(token)) {
                httpResponse.setHeader(AUTH_TOKEN, token);
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "header");
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, token);
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            }
            //sessionIdUrlRewritingEnabled的配置为false,不会在url的后面带上sessionID
            request.setAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED, isSessionIdUrlRewritingEnabled());
            return token;
        }
        return getReferencedSessionId(request, response);
    }
}

​ 如果请求头中的token不存在,则走getReferencedSessionId方法,获取cookie中的信息。

/**
     * shiro默认从cookie中获取sessionId
     *
     * @param request  请求参数
     * @param response 响应参数
     * @return
     */
    private Serializable getReferencedSessionId(ServletRequest request, ServletResponse response) {
        String id = getSessionIdCookieValue(request, response);
        if (id != null) {
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
                    ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE);
        } else {
            //not in a cookie, or cookie is disabled - try the request URI as a fallback (i.e. due to URL rewriting):
            //try the URI path segment parameters first:
            id = getUriPathSegmentParamValue(request, ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
            if (id == null) {
                //not a URI path segment parameter, try the query parameters:
                String name = getSessionIdName();
                id = request.getParameter(name);
                if (id == null) {
                    //try lowercase:
                    id = request.getParameter(name.toLowerCase());
                }
            }
            if (id != null) {
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
                        ShiroHttpServletRequest.URL_SESSION_ID_SOURCE);
            }
        }
        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);
        }
        // always set rewrite flag - SHIRO-361
        request.setAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED, isSessionIdUrlRewritingEnabled());
        return id;
    }

这里验证请求头中的token方法重构完成。

/**
     * 重写父类获取sessionID的方法,若请求为APP或者H5则从请求头中取出token
     *
     * @param request  请求参数
     * @param response 响应参数
     * @return id
     */
    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        if (!(request instanceof HttpServletRequest)) {
            log.debug("Current request is not an HttpServletRequest - cannot get session ID.  Returning null.");
            return null;
        }
        HttpServletRequest httpRequest = WebUtils.toHttp(request);
        if (StringUtils.hasText(httpRequest.getHeader(AUTH_TOKEN))) {
            //从header中获取token
            String token = httpRequest.getHeader(AUTH_TOKEN);
            // 每次读取之后都把当前的token放入response中
            HttpServletResponse httpResponse = WebUtils.toHttp(response);
            if (StringUtils.hasText(token)) {
                httpResponse.setHeader(AUTH_TOKEN, token);
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "header");
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, token);
                request.setAttribute
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Julywhj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值