Spring Security认证成功后回跳(解决前后端分离下OAuth2认证成功回跳)

前言

Spring Security(后面简称SS)用了很长时间了,但之前一直没注意到一个有趣的特性,直到最近弄前后端分离,在OAuth2提供者(github)认证后,需要跳回前端页面(前端页面和服务端不在同个域下),然后突然一般情况下(同域),SS认证后会自动跳回认证前用户想访问的资源。由此开始寻找这个magic。

OAuth2 的一个sso demo,有兴趣可以看一下

问题:SS是怎么在认证成功后自动跳转到认证前用户想访问的资源(url)?

原本我以为SS是跟SS OAuth的实现一样,通过在http报文里面传递这个url,但是看浏览器的报文内容,在认证过程中是没有传递过url的。而且在OAuth里面,OAuth2 provider这种第三方服务,不可能帮你传递这个参数,所以比较好的办法就是利用session,这也解释前后端分离情况下,SS不会(不能)帮我们跳回去前端页面

问题的切口:SavedRequestAwareAuthenticationSuccessHandler

之前我用SS经常接触到这个类,但是我只知道它作为认证成功的handler,在认证成功后会进行一个跳转操作。这里是部分源码

public class SavedRequestAwareAuthenticationSuccessHandler extends
        SimpleUrlAuthenticationSuccessHandler {
    protected final Log logger = LogFactory.getLog(this.getClass());

    private RequestCache requestCache = new HttpSessionRequestCache();

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request,
            HttpServletResponse response, Authentication authentication)
            throws ServletException, IOException {
        //这里取出了一个request
        SavedRequest savedRequest = requestCache.getRequest(request, response);

        if (savedRequest == null) {
            super.onAuthenticationSuccess(request, response, authentication);

            return;
        }
        String targetUrlParameter = getTargetUrlParameter();
        if (isAlwaysUseDefaultTargetUrl()
                || (targetUrlParameter != null && StringUtils.hasText(request
                        .getParameter(targetUrlParameter)))) {
            requestCache.removeRequest(request, response);
            super.onAuthenticationSuccess(request, response, authentication);

            return;
        }

        clearAuthenticationAttributes(request);

        // Use the DefaultSavedRequest URL !!关键的一句!!
        String targetUrl = savedRequest.getRedirectUrl();
        logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);
        // 这里有一个很明显的跳转操作,追踪targetUrl怎么来的
        getRedirectStrategy().sendRedirect(request, response, targetUrl);
    }

    public void setRequestCache(RequestCache requestCache) {
        this.requestCache = requestCache;
    }
}

这里有一个很奇怪的属性——savedRequest,从上面就可以看到,它是来自requestCacherequestCache的类型是HttpSessionRequestCache。这里可以看到,第一行代码就从requestCachegetRequest方法中取出了savedRequest,看一下这个方法

public SavedRequest getRequest(HttpServletRequest currentRequest,
            HttpServletResponse response) {
        HttpSession session = currentRequest.getSession(false);

        if (session != null) {
            return (SavedRequest) session.getAttribute(SAVED_REQUEST);
        }

        return null;
    }

这里就印证了我们前面的猜测,的确是保存在session里面,那么SS什么时候放进去的?毕竟我想要前后端分离下OAuth2认证后跳回前端页面

很容易猜测是调用HttpSessionRequestCachesetRequest方法放进去,可以搜索,这里我是用上面的sso demo的日志找到ExceptionTranslationFiltersendStartAuthentication方法(把日志级别调到debug)

ExceptionTranslationFilter

ExceptionTranslationFilter源码里面注释的第一句话就说明了它的用处

Handles any AccessDeniedException and AuthenticationException
thrown within the filter chain.

这里也解释了各种认证filter诸如UsernamePasswordAuthenticationFilter为什么可以随便抛出AuthenticationException

protected void sendStartAuthentication(HttpServletRequest request,
            HttpServletResponse response, FilterChain chain,
            AuthenticationException reason) throws ServletException, IOException {
        // SEC-112: Clear the SecurityContextHolder's Authentication, as the
        // existing Authentication is no longer considered valid
        SecurityContextHolder.getContext().setAuthentication(null);
        requestCache.saveRequest(request, response);
        logger.debug("Calling Authentication entry point.");
        authenticationEntryPoint.commence(request, response, reason);
    }

到这里大概明白了SS怎么保存和跳转回认证前用户想访问的资源,总结一下。SS通过ExceptionTranslationFilter在认证开始前把request缓存到session中,当认证成功后,在SavedRequestAwareAuthenticationSuccessHandler里取出缓存的request,跳转回认证前用户想访问的url

解决前后端分离下OAuth2认证后跳回前端页面

理解了SS怎么处理认证成功自动跳转问题,解决前后端分离下OAuth2认证成功跳转就很容易了。这里先说一下前后端跨域下OAuth2认证的流程(OAuth2协议具体请自己谷歌,我也说不清楚)

oauth2.png

这个流程比同域OAuth2情况下少了一步,同域下第一步应该是访问一个受保护资源,然后才开始上面流程,所以我暂时的做法是

在/login接口处接受一个auth_url参数,表示认证成功后跳转到这个url,然后认证成功后取出这个url进行跳转

这样只需要修改两处地方

继承OAuth2ClientAuthenticationProcessingFilter

class MyOAuth2ClientAuthenticationProcessingFilter extends OAuth2ClientAuthenticationProcessingFilter{

        private RequestCache requestCache = new HttpSessionRequestCache();

        public MyOAuth2ClientAuthenticationProcessingFilter(String defaultFilterProcessesUrl) {
            super(defaultFilterProcessesUrl);
        }

        @Override
        public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
            requestCache.saveRequest(request, response);
            return super.attemptAuthentication(request, response);
        }
    }

重写这个filter的AuthenticationSuccessHandler

filter.setAuthenticationSuccessHandler((request, response, authentication) -> {
            String authUrl = request.getParameter("auth_url");
            response.sendRedirect(authUrl);
        });

这样就简单粗暴地实现了OAuth2下认证成功后可以自动跳转回认证前想访问的资源了。



作者:zerouwar
链接:https://www.jianshu.com/p/39f46c8de9c1
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

  • 5
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
问题描述: 使用Vue3和Sortable.js实现拖拽排序功能,拖拽排序后重新赋值给原数组,出现回跳问题,即拖拽的元素会到原来的位置。 原因分析: 这是因为拖拽排序后,Sortable.js会将原数组的顺序重新排列,而Vue.js对数组的响应式更新是通过监测数组的变异方法(如push、pop、shift、unshift、splice、sort、reverse等)来实现的,而Sortable.js并没有调用这些变异方法,所以Vue.js并没有检测到数组的变化,从而导致回跳问题。 解决方法: 一种解决方法是手动调用Vue.js的数组变异方法,来更新数组的顺序。具体实现方法如下: 1. 在Vue组件中引入Sortable.js库。 ```javascript import Sortable from 'sortablejs' ``` 2. 在组件的mounted生命周期钩子中初始化Sortable.js实例,并传入需要排序的元素和配置项。 ```javascript mounted() { this.$nextTick(() => { this.sortable = new Sortable(this.$refs.list, { onEnd: this.sortHandler }) }) } ``` 其中,onEnd是Sortable.js的一个调函数,当拖拽结束后会自动调用该函数。 3. 实现sortHandler方法,该方法会在拖拽结束后自动调用,用来更新数组的顺序。 ```javascript sortHandler(event) { const { newIndex, oldIndex } = event const item = this.list.splice(oldIndex, 1)[0] this.list.splice(newIndex, 0, item) } ``` 其中,newIndex和oldIndex分别表示拖拽结束时元素的新索引和旧索引。通过splice方法,将拖拽的元素从旧索引位置删除,再将其插入到新索引位置即可。 4. 在组件的beforeUnmount生命周期钩子中,销毁Sortable.js实例。 ```javascript beforeUnmount() { this.sortable.destroy() } ``` 完整代码示例: ```javascript <template> <div ref="list"> <div v-for="(item, index) in list" :key="item.id"> {{ item.text }} </div> </div> </template> <script> import Sortable from 'sortablejs' export default { data() { return { list: [ { id: 1, text: 'Item 1' }, { id: 2, text: 'Item 2' }, { id: 3, text: 'Item 3' }, { id: 4, text: 'Item 4' }, { id: 5, text: 'Item 5' } ], sortable: null } }, mounted() { this.$nextTick(() => { this.sortable = new Sortable(this.$refs.list, { onEnd: this.sortHandler }) }) }, beforeUnmount() { this.sortable.destroy() }, methods: { sortHandler(event) { const { newIndex, oldIndex } = event const item = this.list.splice(oldIndex, 1)[0] this.list.splice(newIndex, 0, item) } } } </script> ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值