精华内容
下载资源
问答
  • Shiro重构:整合token和cookie实现登陆及验证 认证服务开始只支持PC端的cookie认证方式,因业务需要,要对小程序、H5、App等移动端设备进行认证,这里复用认证服务。由于小程序不支持cookie认证方式,采用token认证...

    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(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);
        }
    
        /**
         * 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;
        }
    
        /**
         * copy from DefaultWebSessionManager
         *
         * @param request  请求参数
         * @param response 响应参数
         * @return
         */
        private String getSessionIdCookieValue(ServletRequest request, ServletResponse response) {
            if (!isSessionIdCookieEnabled()) {
                log.debug("Session ID cookie is disabled - session id will not be acquired from a request cookie.");
                return null;
            }
            if (!(request instanceof HttpServletRequest)) {
                log.debug("Current request is not an HttpServletRequest - cannot get session ID cookie.  Returning null.");
                return null;
            }
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            return getSessionIdCookie().readValue(httpRequest, WebUtils.toHttp(response));
        }
    
        private String getUriPathSegmentParamValue(ServletRequest servletRequest, String paramName) {
            if (!(servletRequest instanceof HttpServletRequest)) {
                return null;
            }
            HttpServletRequest request = (HttpServletRequest) servletRequest;
            String uri = request.getRequestURI();
            if (uri == null) {
                return null;
            }
            int queryStartIndex = uri.indexOf('?');
            if (queryStartIndex >= 0) {
                uri = uri.substring(0, queryStartIndex);
            }
            int index = uri.indexOf(';');
            if (index < 0) {
                //no path segment params - return:
                return null;
            }
            //there are path segment params, let's get the last one that may exist:
            final String TOKEN = paramName + "=";
            uri = uri.substring(index + 1);
            //we only care about the last JSESSIONID param:
            index = uri.lastIndexOf(TOKEN);
            if (index < 0) {
                //no segment param:
                return null;
            }
            uri = uri.substring(index + TOKEN.length());
            index = uri.indexOf(';');
            if (index >= 0) {
                uri = uri.substring(0, index);
            }
            return uri;
        }
    
        private String getSessionIdName() {
            String name = this.getSessionIdCookie() != null ? this.getSessionIdCookie().getName() : null;
            if (name == null) {
                name = ShiroHttpSession.DEFAULT_SESSION_ID_NAME;
            }
            return name;
        }
    

    二、登陆将token设置到响应头中

    我们编写用户登陆接口,在用户登陆时,cookie方式是将sid直接写入到浏览器cookie中,这样前端人员无法获取token值。需要我们将token返回给前端。这里我们将token放到响应头中。

    我们登陆时无cookie和token,当shiro取不到sessionid时,会调用DelegatingSubject类中的getSession(true)方法创建一个新的session.

     public Session getSession(boolean create) {
            if (log.isTraceEnabled()) {
                log.trace("attempting to get session; create = " + create +
                        "; session is null = " + (this.session == null) +
                        "; session has id = " + (this.session != null && session.getId() != null));
            }
    
            if (this.session == null && create) {
    
                //added in 1.2:
                if (!isSessionCreationEnabled()) {
                    String msg = "Session creation has been disabled for the current subject.  This exception indicates " +
                            "that there is either a programming error (using a session when it should never be " +
                            "used) or that Shiro's configuration needs to be adjusted to allow Sessions to be created " +
                            "for the current Subject.  See the " + DisabledSessionException.class.getName() + " JavaDoc " +
                            "for more.";
                    throw new DisabledSessionException(msg);
                }
    
                log.trace("Starting session for host {}", getHost());
                SessionContext sessionContext = createSessionContext();
                Session session = this.securityManager.start(sessionContext);
                this.session = decorate(session);
            }
            return this.session;
        }
    

    会调用Session session = this.securityManager.start(sessionContext);方法,最终调用的是DefaultWebSessionManager中的onStart方法。所以我们要重写这个方法,将产生的sessionid放到response header中.另外当session失效或销毁时的相关方法也需重新实现,具体代码如下:

    ​ 这里我们在存储sid增加了判断逻辑,如请求头中包含设备信息,则将sid存在到响应头中,否则存在到cookie中。

    /**
         * 存储会话id到response header中
         *
         * @param currentId 会话ID
         * @param request   HttpServletRequest
         * @param response  HttpServletResponse
         */
        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);
            }
            String idString = currentId.toString();
            //增加判断,如果请求头中包含DEVICE=MOBILE,则将sessionId放在header中返回
            if (StringUtils.hasText(request.getHeader(DEVICE)) && MOBILE.equals(request.getHeader(DEVICE))) {
                response.setHeader(AUTH_TOKEN, idString);
            } else {
                Cookie template = getSessionIdCookie();
                Cookie cookie = new SimpleCookie(template);
                cookie.setValue(idString);
                cookie.saveTo(request, response);
            }
            log.trace("Set session ID cookie for session with id {}", idString);
        }
    
        /**
         * 设置deleteMe到response header中
         *
         * @param request  request
         * @param response HttpServletResponse
         */
        private void removeSessionIdCookie(HttpServletRequest request, HttpServletResponse response) {
            if (StringUtils.hasText(request.getHeader(AUTH_TOKEN))) {
                response.setHeader(AUTH_TOKEN, Cookie.DELETED_COOKIE_VALUE);
            } else {
                getSessionIdCookie().removeFrom(request, response);
            }
        }
    
        /**
         * 会话创建
         * Stores the Session's ID, usually as a Cookie, to associate with future requests.
         *
         * @param session the session that was just {@link #createSession created}.
         */
        @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);
        }
    
        /**
         * 会话失效
         *
         * @param s   Session
         * @param ese ExpiredSessionException
         * @param key SessionKey
         */
        @Override
        protected void onExpiration(Session s, ExpiredSessionException ese, SessionKey key) {
            super.onExpiration(s, ese, key);
            onInvalidation(key);
        }
    
        @Override
        protected void onInvalidation(Session session, InvalidSessionException ise, SessionKey key) {
            super.onInvalidation(session, 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 cookie.");
                removeSessionIdCookie(WebUtils.getHttpRequest(key), WebUtils.getHttpResponse(key));
            } else {
                log.debug("SessionKey argument is not HTTP compatible or does not have an HTTP request/response " +
                        "pair. Session ID cookie will not be removed due to invalidated session.");
            }
        }
    
        /**
         * 会话销毁
         *
         * @param session Session
         * @param key     SessionKey
         */
        @Override
        protected void onStop(Session session, SessionKey key) {
            super.onStop(session, key);
            if (WebUtils.isHttp(key)) {
                HttpServletRequest request = WebUtils.getHttpRequest(key);
                HttpServletResponse response = WebUtils.getHttpResponse(key);
                log.debug("Session has been stopped (subject logout or explicit stop).  Removing session ID cookie.");
                removeSessionIdCookie(request, response);
            } else {
                log.debug("SessionKey argument is not HTTP compatible or does not have an HTTP request/response " +
                        "pair. Session ID cookie will not be removed due to stopped session.");
            }
        }
    

    最后再在springboot中做如下配置:

     @Bean("sessionManager")
        public SessionManager sessionManager() {
            DefaultWebSessionManager sessionManager = new CustomerWebSessionManager();
            Collection<SessionListener> listeners = new ArrayList<SessionListener>();
            //配置监听
            listeners.add(sessionListener());
            sessionManager.setSessionListeners(listeners);
    
            Cookie sessionIdCookie = sessionManager.getSessionIdCookie();
            sessionIdCookie.setName("sid");
            sessionManager.setGlobalSessionTimeout(EXPIRE_SECOND * 1000); // 超时时间
            sessionManager.setDeleteInvalidSessions(true);
            sessionManager.setSessionIdCookie(sessionIdCookie);
            sessionManager.setSessionDAO(sessionDAO());
            sessionManager.setCacheManager(cacheManager());
    
            //全局会话超时时间(单位毫秒),默认30分钟  暂时设置为10秒钟 用来测试
            sessionManager.setGlobalSessionTimeout(EXPIRE_SECOND * 1000);
            //是否开启删除无效的session对象  默认为true
            sessionManager.setDeleteInvalidSessions(true);
            //是否开启定时调度器进行检测过期session 默认为true
            sessionManager.setSessionValidationSchedulerEnabled(true);
            //设置session失效的扫描时间, 清理用户直接关闭浏览器造成的孤立会话 默认为 1个小时
            //设置该属性 就不需要设置 ExecutorServiceSessionValidationScheduler 底层也是默认自动调用ExecutorServiceSessionValidationScheduler
            sessionManager.setSessionValidationInterval(600000);
            //取消url 后面的 JSESSIONID
            sessionManager.setSessionIdUrlRewritingEnabled(false);
            return sessionManager;
        }
    

    我们使用PostMain测试下:

    WX20211016-171120@2x

    WX20211016-171120@2x

    我们使用token测试下能否通过验证:

    WX20211016-171425@2x

    WX20211016-171425@2x

    可以看到验证通过。使用错误的token验证下:

    WX20211016-171530@2x

    WX20211016-171530@2x

    我们将token的最后一位2改成3后,验证失败。需要重新登陆。

    自此我们改造完成。附上完整代码:

    /**
     * 兼容 token 和 cookie
     *
     * @Author julyWhj
     * @Description $
     * @Date 2021/10/16 3:13 下午
     **/
    @Slf4j
    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);
        }
    
        /**
         * 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;
        }
    
        /**
         * copy from DefaultWebSessionManager
         *
         * @param request  请求参数
         * @param response 响应参数
         * @return
         */
        private String getSessionIdCookieValue(ServletRequest request, ServletResponse response) {
            if (!isSessionIdCookieEnabled()) {
                log.debug("Session ID cookie is disabled - session id will not be acquired from a request cookie.");
                return null;
            }
            if (!(request instanceof HttpServletRequest)) {
                log.debug("Current request is not an HttpServletRequest - cannot get session ID cookie.  Returning null.");
                return null;
            }
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            return getSessionIdCookie().readValue(httpRequest, WebUtils.toHttp(response));
        }
    
        private String getUriPathSegmentParamValue(ServletRequest servletRequest, String paramName) {
            if (!(servletRequest instanceof HttpServletRequest)) {
                return null;
            }
            HttpServletRequest request = (HttpServletRequest) servletRequest;
            String uri = request.getRequestURI();
            if (uri == null) {
                return null;
            }
            int queryStartIndex = uri.indexOf('?');
            if (queryStartIndex >= 0) {
                uri = uri.substring(0, queryStartIndex);
            }
            int index = uri.indexOf(';');
            if (index < 0) {
                //no path segment params - return:
                return null;
            }
            //there are path segment params, let's get the last one that may exist:
            final String TOKEN = paramName + "=";
            uri = uri.substring(index + 1);
            //we only care about the last JSESSIONID param:
            index = uri.lastIndexOf(TOKEN);
            if (index < 0) {
                //no segment param:
                return null;
            }
            uri = uri.substring(index + TOKEN.length());
            index = uri.indexOf(';');
            if (index >= 0) {
                uri = uri.substring(0, index);
            }
            return uri;
        }
    
        private String getSessionIdName() {
            String name = this.getSessionIdCookie() != null ? this.getSessionIdCookie().getName() : null;
            if (name == null) {
                name = ShiroHttpSession.DEFAULT_SESSION_ID_NAME;
            }
            return name;
        }
    
    
        /**
         * 存储会话id到response header中
         *
         * @param currentId 会话ID
         * @param request   HttpServletRequest
         * @param response  HttpServletResponse
         */
        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);
            }
            String idString = currentId.toString();
            //增加判断,如果请求头中包含DEVICE=MOBILE,则将sessionId放在header中返回
            if (StringUtils.hasText(request.getHeader(DEVICE)) && MOBILE.equals(request.getHeader(DEVICE))) {
                response.setHeader(AUTH_TOKEN, idString);
            } else {
                Cookie template = getSessionIdCookie();
                Cookie cookie = new SimpleCookie(template);
                cookie.setValue(idString);
                cookie.saveTo(request, response);
            }
            log.trace("Set session ID cookie for session with id {}", idString);
        }
    
        /**
         * 设置deleteMe到response header中
         *
         * @param request  request
         * @param response HttpServletResponse
         */
        private void removeSessionIdCookie(HttpServletRequest request, HttpServletResponse response) {
            if (StringUtils.hasText(request.getHeader(AUTH_TOKEN))) {
                response.setHeader(AUTH_TOKEN, Cookie.DELETED_COOKIE_VALUE);
            } else {
                getSessionIdCookie().removeFrom(request, response);
            }
        }
    
        /**
         * 会话创建
         * Stores the Session's ID, usually as a Cookie, to associate with future requests.
         *
         * @param session the session that was just {@link #createSession created}.
         */
        @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);
        }
    
        /**
         * 会话失效
         *
         * @param s   Session
         * @param ese ExpiredSessionException
         * @param key SessionKey
         */
        @Override
        protected void onExpiration(Session s, ExpiredSessionException ese, SessionKey key) {
            super.onExpiration(s, ese, key);
            onInvalidation(key);
        }
    
        @Override
        protected void onInvalidation(Session session, InvalidSessionException ise, SessionKey key) {
            super.onInvalidation(session, 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 cookie.");
                removeSessionIdCookie(WebUtils.getHttpRequest(key), WebUtils.getHttpResponse(key));
            } else {
                log.debug("SessionKey argument is not HTTP compatible or does not have an HTTP request/response " +
                        "pair. Session ID cookie will not be removed due to invalidated session.");
            }
        }
    
        /**
         * 会话销毁
         *
         * @param session Session
         * @param key     SessionKey
         */
        @Override
        protected void onStop(Session session, SessionKey key) {
            super.onStop(session, key);
            if (WebUtils.isHttp(key)) {
                HttpServletRequest request = WebUtils.getHttpRequest(key);
                HttpServletResponse response = WebUtils.getHttpResponse(key);
                log.debug("Session has been stopped (subject logout or explicit stop).  Removing session ID cookie.");
                removeSessionIdCookie(request, response);
            } else {
                log.debug("SessionKey argument is not HTTP compatible or does not have an HTTP request/response " +
                        "pair. Session ID cookie will not be removed due to stopped session.");
            }
        }
    }
    
    展开全文
  • cookie token 都存放在 header 中,为什么不会劫持 tokencookie:登录后服务端生成的sessionid,并在http请求里返回到客户端,同时服务端保存sessionid,以后客户端的每次http请求都带上cookie(sessionid),...

    cookie 和 token 都存放在 header 中,为什么不会劫持 token?

    cookie:登录后服务端生成的sessionid,并在http请求里返回到客户端,同时服务端保存sessionid,以后客户端的每次http请求都带上cookie(sessionid),服务端会获取cookie(sessionid)然后验证用户的身份。所以拿到cookie就拿到了sessionid,就可验证通过。同时浏览器会自动携带cookie;

    token:同样是登录后服务端返回一个token,客户端保存起来,在以后http请求里手动的加入到请求头里,服务端根据token 进行身份的校验。浏览器不会自动携带token。

    CSRF 跨站点请求伪造:通过浏览器会自动携带同域cookie的特点。cookie的传递流程是用户在访问站点时,服务器端生成cookie,发送给浏览器端储存,当下次再访问时浏览器会将该网站的cookie发回给服务器端

    如果用户登陆了A网站,拿到了cookie,又点击了恶意的网站B。

    B收到请求以后,返回一段攻击代码,并且发出一个请求给网站A。

    浏览器会在用户不知情的情况下,根据B的请求,带着cookie访问A。

    由于HTTP是无状态的,A网站不知道这个请求其实是恶意网站B发出的,就会根据cookie来处理请求,从而执行了攻击代码。

    而浏览器不会自动携带 token,所以不会劫持 token。

    XSS:跨站脚本工攻击是指通过存在安全漏洞的Web网站注册用户的浏览器内运行非法的HTML标签或者JavaScript进行的一种攻击

    cookie和token都是不安全的

    token和cookie的区别

    HTTP协议本身是无状态的,所以需要一个标志来对用户身份进行验证

    1、cookie

    用户登录成功后,会在服务器存一个session,同时发送给客户端一个cookie,这个cookie里面有唯一标识该用户的sessionID

    数据需要客户端和服务器同时存储

    用户再进行请求操作时,需要带上cookie,在服务器进行验证

    cookie是有状态的

    2、token

    用户进行任何操作时,都需要带上一个token

    token的存在形式有很多种,header/requestbody/url 都可以

    这个token只需要存在客户端,服务器在收到数据后,进行解析

    token是无状态的

    token相对cookie的优势

    1、支持跨域访问 ,将token置于请求头中,而cookie是不支持跨域访问的;

    2、无状态化, 服务端无需存储token ,只需要验证token信息是否正确即可,而session需要在服务端存储,一般是通过cookie中的sessionID在服务端查找对应的session;

    3、 无需绑定到一个特殊的身份验证 方案(传统的用户名密码登陆),只需要生成的token是符合我们预期设定的即可;

    4、 更适用于移动端 (Android,iOS,小程序等等),像这种原生平台不支持cookie,比如说微信小程序,每一次请求都是一次会话,当然我们可以每次去手动为他添加cookie,详情请查看博主另一篇博客;

    5、 避免CSRF跨站伪造攻击 ,还是因为不依赖cookie;

    6、 非常适用于RESTful API ,这样可以轻易与各种后端(java,.net,python…)相结合,去耦合

    展开全文
  • cookie的由来 由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。 怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带 自己通行证。这样服务器就能从通行证上确认客户身份...

    cookie的由来

    由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。
    怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带
    自己通行证。这样服务器就能从通行证上确认客户身份了
    
    cookie指的就是在浏览器里面存储的一种数据,仅仅是浏览器实现的一种数据
    存储功能。cookie的保存时间,可以自己在程序中设置。如果没有设置保存时
    间,应该是一关闭浏览器,cookie就自动消失。
    
    Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录
    该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器
    会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该
    Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务
    器还可以根据需要修改Cookie的内容。
    
    
    
    Cookie会根据从服务器端发送的响应报文内的一个叫做Set-Cookie的首部字段信息,
    通知客户端保存Cookie。在同源策略下当浏览器再请求服务器时,
    浏览器把请求的网址连同该Cookie一同提交给服务器。
    服务器通过检查Cookie来获取用户状态。
    Cookie一般是被浏览器以txt纯文本的形式存储在电脑硬盘中
    

    session

    Session是另一种记录客户状态的机制,不同的是Cookie
    保存在客户端浏览器中,而Session保存在服务器上
    
    如果说Cookie机制是通过检查客户身上的“通行证”来确定客户身份的话,
    那么Session机制就是通过检查服务器上的“客户明细表”来确认客户身份
    
    session有一个缺陷:如果web服务器做了负载均衡
    那么下一个操作请求到了另一台服务器的时候session会丢失。
    
    Session的使用比Cookie方便,但是过多的Session存储在服务器内存中,
    会对服务器造成压力。
    

    Cookie与Session的区别和联系

    1、 cookie数据存放在客户的浏览器上,session数据放在服务器上
    2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行
    	 COOKIE欺骗,考虑到安全应当使用session
    3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能。
    	考虑到减轻服务器性能方面,应当使用COOKIE
    4、单个cookie在客户端的限制是3K,就是说一个站点在客户端存放的COOKIE不能超过3K	 
    
    Cookie和Session的方案虽然分别属于客户端和服务端,但是服务端的session的实现对客
    户端的cookie有依赖关系的,上面我讲到服务端执行session机制时候会生成session
    的id值,这个id值会发送给客户端,客户端每次请求都会把这个id值放到http请求的头部
    发送给服务端,而这个id值在客户端会保存下来,保存的容器就是cookie,因此当我们完
    全禁掉浏览器的cookie的时候,服务端的session也会不能正常使用。
    

    token

    在大多数使用Web API的互联网公司中,tokens 是多用户下处理认证的最佳方式

    以下几点特性会让你在程序中使用基于Token的身份验证
    
    1.无状态、可扩展
    
    2.支持移动设备
    
    3.跨程序调用
    
    4.安全
    

    项目中使用token总结

    使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。
    
    1.前端使用用户名跟密码请求首次登录
    
    2.后服务端收到请求,去验证用户名与密码是否正确
    
    3.验证成功后,服务端会根据用户id、用户名、定义好的秘钥、
    	过期时间生成一个 Token,再把这个 Token 发送给前端
    
    4.前端收到 返回的Token ,把它存储起来,比如放在 Cookie 里或者 Local Storage 里
    
    5.前端每次路由跳转,判断 localStroage 有无 token ,没有则跳转到登录页。
    	有则请求获取用户信息,改变登录状态
    	
    6.前端每次向服务端请求资源的时候需要在请求头里携带服务端签发的Token
    
    7.服务端收到请求,然后去验证前端请求里面带着的 Token。没有或者 token 过期,
    	返回401。如果验证成功,就向前端返回请求的数据。
    
    8.前端得到 401 状态码,重定向到登录页面。
    
    

    token的起源

    基于服务器的验证
    我们都是知道HTTP协议是无状态的,这种无状态意味着程序需要验证每一次请求,
    从而辨别客户端的身份。
    
    在这之前,程序都是通过在服务端存储的登录信息来辨别请求的
    。这种方式一般都是通过存储Session来完成。
    
    基于服务器验证方式暴露的一些问题
    1.Seesion:每次认证用户发起请求时,服务器需要去创建一个记录来存储信息。
    当越来越多的用户发请求时,内存的开销也会不断增加。
    
    2.可扩展性:在服务端的内存中使用Seesion存储登录信息,伴随而来的是可扩展性问题。
    
    3.CORS(跨域资源共享):当我们需要让数据跨多台移动设备上使用时,跨域资源的共享会
    是一个让人头疼的问题。在使用Ajax抓取另一个域的资源,就可以会出现禁止请求的情况。
    
    4.CSRF(跨站请求伪造):用户在访问银行网站时,他们很容易受到跨站请求伪造的攻击,
    并且能够被利用其访问其他的网站。
    

    点击进入原网址查看详细介绍

    展开全文
  • 什么是token

    Token是什么

    • Token 其实就是访问资源的凭证

    • 一般是用户通过用户名密码登录成功之后,服务器将登录凭证作为数字签名,加密之后得到的字符串就是 Token

    区别

    • Token 在用户登录成功之后返回给客户端,客户端组要有三种存储方式

      1. 储存在 localStorage 中,每次调用接口时放在http请求头里面,长期有效

      2. 储存在 sessionStorage 中,每次调用接口时,把它当为一个字段传给后台,浏览器关闭自动清除

      3. 储存在 cookie 中,每次调用接口会自动发送,不过缺点是不能跨域

    深入了解

    • 将 Token 存储在 webStorage(localStorage,sessionStorage) 中可以通过同域的js访问,这样导致很容易受到 xss 攻击,特别是项目中引入很多第三方js库的情况下,如果js脚本被盗用,攻击者就可以轻易访问你的网站。

    xss攻击:是一种注入代码攻击,通过在网站里注入script代码,当访问者浏览网站的时候通过注入的script代码窃取用户信息,盗用用户身份等

    • 将 Token 存储在 cookie 中,可以指定 httponly 来防止 js 被读取,也可以指定 secure 来保证 Token 只在 HTTPS 下传输,缺点是不符合 RestFul 最佳实践,容易受到 CSRF 攻击。

    CSRF: 跨站点请求伪造,攻击者盗用已经认证过的用户信息,以用户信息的名义进行操作(转账,购买商品等),由于身份已经认证过了,所以网站会认为此操作是用户本人操作。 CSRF 并不能拿到用户信息,但它可以盗用用户的凭证进行操作。

    展开全文
  • token为什么就比cookie安全 cookie是什么: 登陆后,后端生成一个sessionid放在cookie中返回给客户端,并且服务端一直记录着这个sessionid,客户端以后每次请求都会自动带上这个sessionid,服务端通过这个session...
  • tokencookie区别 token和cookie一样都是首次登陆时,由服务器下发,都是当交互时进行验证的功能,作用都是为无状态的HTTP提供的持久机制。 token存在哪儿都行,localstorage或者cookietoken和cookie...
  • cookie、Session、Token、sessionStorage、localStorage简介 cookie 是一个非常具体的东西,只得是浏览器里永久存储的一种数据,是浏览器实现的一种数据存储功能。Cookie在计算机中是个存储在浏览器目录中的文本文件...
  • Token,Cookie,Sessionsso

    千次阅读 2021-08-26 11:05:55
    Token(服务端生产字符串作为令牌,JWT...其中服务器上token设置一个有效期,每次APP请求的时候都验证token和有效期。 Cookie(保存在客户端),Session(保存在服务端HttpSession) 单点登录(Single Sign On)SSO 第一次通过
  • 移动互联网的繁荣,出现很多客户端,不同于以往的...面对这两个问题,出现了token的验证方式,下面均益数一下三者的区别和问题,以及解决方案。cookie:cookie是存在浏览器的标识用户的方式,由服务端为每一个用户...
  • 是计算机术语:令牌,令牌是一种能够控制站点占有媒体的特殊帧,以区别数据帧及其他控制帧。token其实说的更通俗点可以叫暗号,在一些数据传输之前,要先进行暗号的核对,不同的暗号被授权不同的数据操作。基于 ...
  • cookie和token区别

    2021-03-28 10:14:51
    目录首先看看cookie和session的用途What is TokenToken与session+cookie区别 cookie和session都是会话保持技术的解决方案,随着技术的发展,token机制出现在了我们面前,有木有人傻傻分不清,我就是0.0。记录一下...
  • 请求中的cookie、session和token

    多人点赞 2021-05-09 11:06:48
    token和前两者不同; session和cookie 其目的是在http的无状态性下,为了使某个域名下的所有网页能够共享某些数据产生的一种手段。它们在客户端对服务端的访问中的运作方式如下: 客户端发送的请求在被服务器端...
  • 1.cookie,session,token的出现的背景 很久很久以前,Web 基本上就是文档的浏览而已, 既然是浏览,作为服务器, 不需要记录谁在某一段时间里都浏览了什么文档,每次请求都是一个新的HTTP协议, 就是请求加响应, ...
  • Java中session、cookietoken区别 >>>>>>> 一.Cookie 1.什么是cookie? Cookie 技术产生源于 HTTP 协议在互联网上的急速发展。随着互联网时代的策马奔腾,带宽等限制不存在了,人们需要更...
  • 安装 js-cookie npm install js-cookie -s 在src下面创建util文件夹,并创建auth.js文件 import Cookies from 'js-cookie' const TokenKey = 'token' export function getToken() { return Cookies.get(Token...
  • 前端进行数据请求有:普通的ajax(json)请求,jsop跨域请求,cors跨域请求,fetch请求…PC端这些请求方式中,普通的ajax(json)请求jsop跨域请求是默认携带cookie的,而cors跨域请求fetch请求默认是不携带cookie的...
  • cookie:登录后服务端生成的sessionid,并在http请求里返回到...token:同样是登录后服务端返回一个token,客户端保存起来,在以后http请求里手动的加入到请求头里,服务端根据token 进行身份的校验。浏览器不会自动
  • 如果要测试含有token鉴权的接口的流程: - 1.... - 2.通过登录接口 得到用户信息,token, session, - 3....含有token的接口,每个接口都要传token的,否则接口测...cookie域名、IP绑定在一起的 token vs sessio..
  • 在前后端分离的项目里,我们请求接口的流程一般是:用户使用用户名密码登录信息正确,接口返回token请求需要登录验证的接口,将token放到header里一起请求接口这里介绍一下,在webapi项目里,token是怎么生成的项目...
  • session和token认证方式,cookie和localStorage存储,Web安全 ——总结 目录 1.web服务的安全问题 1.1 XSS 1.2 CSRF 1.3 同源策略 1.4 跨域访问 2.认证方式 2.1 token和session,两种认证方式 2.2 存token的解决方案...
  • goLogin(){ this.$refs['formInline'].validate((valid) => { if (valid) { //打开加载中 this.loading = true // 传参 let str={ email:this.formInline.email, passwo..
  • Cookie、Session与Token之间的关系 Cookie cookie是保存在本地终端的数据。cookie由服务器生成,发送给浏览器,浏览器把cookie以key-value形式保存到某个目录下的文本文件内,下一次请求同一网站时会把该cookie发送...
  • 对于用来登录的cookie,有两种常见的方式可以将登录信息存储在cookie里面:一种是签名(signed)cookie,另一种是令牌(token)cookie 签名cookie通常会存储用户名,可能还有用户ID、用户最后一次成功登录的时间,以及...
  • 在某次测试中注意到网站的CSRF的TOKEN放在Cookie中,感觉之前的一些知识起了冲突。遂查了些资料整理明白了,并记录于此文。 0x02 CSRF与CSRF Token 相信有很多师傅的知识都是通过道哥的《白帽子讲WEB安全》中...
  • 实际的工作场景中,有很多接口之间是有依赖关系的,这里列举两个最简单的例子,cookie和token。话不多说,上代码,因为也比较简单,就没写注释。 import requests def token_login(): url = "这里面是返回token的...
  • cookie、session和token区别

    千次阅读 2021-12-02 21:26:48
    cookie、session和token的特点和区别,HTTP状态
  • jmeter请求之cookie和token处理的两种方式方式一 添加http cookie管理器方式二 添加http信息头管理器 方式一 添加http cookie管理器 右键线程组,选择配置元件添加HTTP Cookie 管理器,注意要放在线程组最上方 方式...
  • 随着技术的发展,Token机制出现在我们面前,不过很多开发者对于Token和Cookie、Session的区别及使用场景分辨不清。 Cookie和Session的用途 要知道我们访问网站都是通过HTTP协议或HTTPS协议来完成的,HTTP协议它本身...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 85,610
精华内容 34,244
关键字:

token和cookie的区别