问题背景

在做Oauth2Sso单点登录时,需要在client.szile.com域名下请求一个需要登录才能访问的接口/secure/data,会302到oauth.szile.com的/oauth/authorize接口上进行认证、/oauth/authorize接口又会302到/login接口到登录页,在登录页使用表单登录成功后再302到client.szile.com域名下。

问题的产生

   问题是如果接口请求都是浏览器地址栏发出的一切都OK,但是如果使用axios发出的请求就不行🙅‍♂️,why ??? 

在这里插入图片描述
在这里插入图片描述

问题的原因

反复的对比发现,使用axios发请求后,Response中没有设置Cookie。
Cookie的值是SessionId,Oauth2Sso就是根据session中记录了登录成功后需要跳转的信息「client_id、redirect_uri、response_type、scope、state」。因为axios的请求没有设置Cookie,所以登录成功后无法找到进行跳转的信息,所以使用axios不会成功。

👌OK,找到问题原因了 ,想办法解决吧,怎么才能让Response设置Cookis呢?

定位与解决

就想到了Filter。

【1】 Cookie如何存储的?

SessionRepositoryFilter 是管理Session存储的。
org.springframework.session.web.http.SessionRepositoryFilter.SessionRepositoryRequestWrapper#commitSession 是提交Session的方法。而commitSession方法中

private void commitSession() {
            SessionRepositoryFilter<S>.SessionRepositoryRequestWrapper.HttpSessionWrapper wrappedSession = this.getCurrentSession();
            // 只有wrappedSesson 不为空时才会保存Cookie
            if (wrappedSession == null) {
                if (this.isInvalidateClientSession()) {
                    SessionRepositoryFilter.this.httpSessionIdResolver.expireSession(this, this.response);
                }
            } else {
                S session = wrappedSession.getSession();
                this.clearRequestedSessionCache();
                SessionRepositoryFilter.this.sessionRepository.save(session);
                String sessionId = session.getId();
                if (!this.isRequestedSessionIdValid() || !sessionId.equals(this.getRequestedSessionId())) {
                    SessionRepositoryFilter.this.httpSessionIdResolver.setSessionId(this, this.response, sessionId);
                }
            }

        }

是设置sessionId的。这里的httpSessionIdResolver默认实现是CookieHttpSessionIdResolver。
org.springframework.session.web.http.CookieHttpSessionIdResolver#setSessionId

public void setSessionId(HttpServletRequest request, HttpServletResponse response, String sessionId) {
        if (!sessionId.equals(request.getAttribute(WRITTEN_SESSION_ID_ATTR))) {
            request.setAttribute(WRITTEN_SESSION_ID_ATTR, sessionId);
            // 设置Cookie
            this.cookieSerializer.writeCookieValue(new CookieValue(request, response, sessionId));
        }
    }
【2】Session如何产生的?

由【1】知道只有wrappedSesson 不为空时才会保存Session到Cookie中。那么Session是在何时产生的呢?条件是什么?
debug定位到
org.springframework.security.web.access.ExceptionTranslationFilter#handleSpringSecurityException -> org.springframework.security.web.access.ExceptionTranslationFilter#sendStartAuthentication -> this.requestCache.saveRequest(request, response);

protected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, AuthenticationException reason) throws ServletException, IOException {
        SecurityContextHolder.getContext().setAuthentication((Authentication)null);
        // 保存request。其中创建的Session
        this.requestCache.saveRequest(request, response);
        this.logger.debug("Calling Authentication entry point.");
        this.authenticationEntryPoint.commence(request, response, reason);
    }

org.springframework.security.web.savedrequest.HttpSessionRequestCache#saveRequest

public void saveRequest(HttpServletRequest request, HttpServletResponse response) {
		// 创建Session的条件
        if (this.requestMatcher.matches(request)) {
            DefaultSavedRequest savedRequest = new DefaultSavedRequest(request, this.portResolver);
            if (this.createSessionAllowed || request.getSession(false) != null) {
                // getSession() 方法中创建的Session
                request.getSession().setAttribute(this.sessionAttrName, savedRequest);
                this.logger.debug("DefaultSavedRequest added to Session: " + savedRequest);
            }
        } else {
            this.logger.debug("Request not saved as configured RequestMatcher did not match");
        }

    }

最终有org.springframework.session.web.http.SessionRepositoryFilter.SessionRepositoryRequestWrapper#getSession(boolean)创建。
而创建的条件是this.requestMatcher.matches(request)。
在这里插入图片描述
最终定位到关键点是 Request的headers.Accept中不能包含application/json。

解决

所以在axios请求是设置header

headers: { 'Accept': 'text/html,application/xhtml+xml,application/xml,text/plain,*/*' }

这样就可以创建Session,并且设置Cookie。
在这里插入图片描述

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐