记录一次shiro排错(会话无效)

记录shiro 的一次bug排除

用户在登陆成功之后,访问项目主页会话无效提示需要再次登陆

菜鸡解决问题第一招 gooogle

1,shiro是如何进行会话验证的?

并没有查到有效的信息,大都数都是说分析Shiro的源码,并没有说明到底前端与服务器交互,是如何判断此次会话有效,(因为着急解决bug的心理,也没细看,就想找到直接答案).因为对于Web 服务sesseion理解不深刻,

并不知道JSESSIONID 的存在(shiro在登陆时会在cookie中加入key为JSESSIONID 的唯一标识)。

session的工作原理

(1)当一个session第一次被启用时,一个唯一的标识被存储于本地的cookie中。

(2)服务端会保存此唯一标识,前端每次请求会在cookie里带上这唯一标识进行会话判断

菜鸡解决问题第二招 请教大佬

1,最后解决方案是在login接口的时候吧sessionid 通过JWT加密传给前端

2,重写DefaultWebSessionManager类 的getSessionId 方法

public class CustomSessionManager extends DefaultWebSessionManager {

    private static final String AUTHORIZATION = "Authorization";
    private static final String COOKIE = "Cookie";

    private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";

    public CustomSessionManager() {
        super();
    }

    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        // 获取requetstHeader中 Authorization 的值
        String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
		 // 获取请求链接中 token 的值
        if (StringUtils.isEmpty(id)) {
            id = request.getParameter("token");
        }
         // 获取请求链接中Cookie中 Authorization 的值
        if(StringUtils.isEmpty(id)){
            HttpServletRequest httpRequest = WebUtils.toHttp(request);
            Cookie[] cookies = httpRequest.getCookies();
            if (cookies != null){
                for (Cookie c: cookies) {
                      if (AUTHORIZATION.equals(c.getName())){
                            id = c.getValue();
                      }
                }
            }
        }
        //如果请求头中有 Authorization 则其值为sessionId
        if (!StringUtils.isEmpty(id)){
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            try {
                return JwtUtils.verifyToken(id).get("token").asString();
            } catch (Exception ex) {
                return super.getSessionId(request, response);
            }
        } else {
            //否则按默认规则从cookie取sessionId
            return super.getSessionId(request, response);
        }
    }
}
菜鸡决定要学习

重写之后虽然暂时能够解决这个bug,但是没有找到事情的原因啊(抓狂ing)

于是自己就尝试去看了Shiro官网的文档关于SessionManagement

Shiro 官网 强烈推荐大家用google翻译,以前总是不想用。靠自己蹩脚的英语水平去理解,最后也是是懂非懂,所以这里夸一下google翻译真心翻译的很棒。

最后找到JSESSIONID 这个关键点,发现是前端没有传这个值 。

但是我不甘心啊。于是决定去一探Shiro的源码。概念都在官网上,这里盘代码,

就从这三句代码开始

// subject 是与当前线程进行绑定的,简单理解就是用户,但是官方说的是用户视图(包含用户的权限,角色),
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken =
                    new UsernamePasswordToken(user.getName(), password);subject.login(usernamePasswordToken);        

直接进login方法然后一步步进去,这里就不多加剩余的代码,直接撸到什么地方生成JSESSIONID ,什么时候

加到Cookie中。

  • 服务端生成sessionId并保存
// 类路径
 org\crazycake\shiro-redis\3.0.0\shiro-redis-3.0.0.jar!\org\crazycake\shiro\RedisSessionDAO.class
    /**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis
     * 使用的是shiro-redis开源插件
     */
@Override
protected Serializable doCreate(Session session) {
   if (session == null) {
      logger.error("session is null");
      throw new UnknownSessionException("session is null");
   }
   // 服务器段保存改sessionId 保存在redis中
   Serializable sessionId = this.generateSessionId(session);  
       this.assignSessionId(session, sessionId);
       this.saveSession(session);
   return sessionId;
}
  • Session 管理器设置Cookie名字
// org.apache.shiro.web.session.mgt.DefaultWebSessionManager
public DefaultWebSessionManager() {
    // JSESSIONID 
    Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
    cookie.setHttpOnly(true); //more secure, protects against XSS attacks
    this.sessionIdCookie = cookie;
    this.sessionIdCookieEnabled = true;
    this.sessionIdUrlRewritingEnabled = true;
}
  • 将sessionId 设置到request Cookie 中
/** org.apache.shiro.session.mgt.eis.AbstractSessionDAO **/
protected void assignSessionId(Session session, Serializable sessionId) {
    ((SimpleSession) session).setId(sessionId);
}

//org.apache.shiro.web.session.mgt.DefaultWebSessionManager
   @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/response " +
                    "pair. No session ID cookie will be set.");
            return;

        }
        HttpServletRequest request = WebUtils.getHttpRequest(context);
        HttpServletResponse response = WebUtils.getHttpResponse(context);

        if (isSessionIdCookieEnabled()) {
            Serializable sessionId = session.getId();
            // 下面那个方法
            storeSessionId(sessionId, request, response);
        } else {
            log.debug("Session ID cookie is disabled.  No cookie has been set for new session with id {}", session.getId());
        }
        request.removeAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE);
        request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_IS_NEW, Boolean.TRUE);
    }

// 保存sessionid
private void storeSessionId(Serializable currentId, HttpServletRequest request, HttpServletResponse response) {
    if (currentId == null) {
        String msg = "sessionId cannot be null when persisting for subsequent requests.";
        throw new IllegalArgumentException(msg);
    }
    Cookie template = getSessionIdCookie();
    Cookie cookie = new SimpleCookie(template);
    String idString = currentId.toString();
    cookie.setValue(idString);
    cookie.saveTo(request, response);
    log.trace("Set session ID cookie for session with id {}", idString);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值