1. 首页
  2. IT资讯

Spring Security 前后端分离登录,非法请求直接返回 JSON

“u003Cdivu003Eu003Cpu003E今天要和小伙伴们聊一聊 Spring Security 中的另外一个问题,那就是在 Spring Security 中未获认证的请求默认会重定向到登录页,但是在前后端分离的登录中,这个默认行为则显得非常不合适,今天我们主要来看看如何实现未获认证的请求直接返回 JSON ,而不是重定向到登录页面。u003Cu002Fpu003Eu003Cpu003E大家知道,在自定义 Spring Security 配置的时候,有这样几个属性:u003Cu002Fpu003Eu003Cpreu003E@Overrideu003Cbru003Eprotected void configure(HttpSecurity http) throws Exception {u003Cbru003E http.authorizeRequests()u003Cbru003E .anyRequest().authenticated()u003Cbru003E .formLogin()u003Cbru003E .loginProcessingUrl(“u002FdoLogin”)u003Cbru003E .loginPage(“u002Flogin”)u003Cbru003E u002Fu002F其他配置u003Cbru003E .permitAll()u003Cbru003E .and()u003Cbru003E .csrf().disable();u003Cbru003E}u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E这里有两个比较重要的属性:u003Cu002Fpu003Eu003Culu003Eu003Cliu003EloginProcessingUrl:这个表示配置处理登录请求的接口地址,例如你是表单登录,那么 form 表单中 action 的值就是这里填的值。u003Cu002Fliu003Eu003Cliu003EloginPage:这个表示登录页的地址,例如当你访问一个需要登录后才能访问的资源时,系统就会自动给你通过重定向跳转到这个页面上来。u003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003E这种配置在前后端不分的登录中是没有问题的,在前后端分离的登录中,这种配置就有问题了。我举个简单的例子,例如我想访问 u002Fhello 接口,但是这个接口需要登录之后才能访问,我现在没有登录就直接去访问这个接口了,那么系统会给我返回 302,让我去登录页面,在前后端分离中,我的后端一般是没有登录页面的,就是一个提示 JSON,例如下面这样:u003Cu002Fpu003Eu003Cpreu003E@GetMapping(“u002Flogin”)u003Cbru003Epublic RespBean login() {u003Cbru003E return RespBean.error(“尚未登录,请登录!”);u003Cbru003E}u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E也就是说,当我没有登录直接去访问 u002Fhello 这个接口的时候,我会看到上面这段 JSON 字符串。在前后端分离开发中,这个看起来没问题(后端不再做页面跳转,无论发生什么都是返回 JSON)。但是问题就出在这里,系统默认的跳转是一个重定向,就是说当你访问 u002Fhello 的时候,服务端会给浏览器返回 302,同时响应头中有一个 Location 字段,它的值为 http:u002Fu002Flocalhost:8081u002Flogin ,也就是告诉浏览器你去访问 http:u002Fu002Flocalhost:8081u002Flogin 地址吧。浏览器收到指令之后,就会直接去访问 http:u002Fu002Flocalhost:8081u002Flogin 地址,如果此时是开发环境并且请求还是 Ajax 请求,就会发生跨域。因为前后端分离开发中,前端我们一般在 NodeJS 上启动,然后前端的所有请求通过 NodeJS 做请求转发,现在服务端直接把请求地址告诉浏览器了,浏览器就会直接去访问 http:u002Fu002Flocalhost:8081u002Flogin 了,而不会做请求转发了,因此就发生了跨域问题。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E解决方案u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E很明显,上面的问题我们不能用跨域的思路来解决,虽然这种方式看起来也能解决问题,但不是最佳方案。u003Cu002Fpu003Eu003Cpu003E如果我们的 Spring Security 在用户未获认证的时候去请求一个需要认证后才能请求的数据,此时不给用户重定向,而是直接就返回一个 JSON,告诉用户这个请求需要认证之后才能发起,就不会有上面的事情了。u003Cu002Fpu003Eu003Cpu003E这里就涉及到 Spring Security 中的一个接口 AuthenticationEntryPoint,该接口有一个实现类:LoginUrlAuthenticationEntryPoint ,该类中有一个方法 commence,如下:u003Cu002Fpu003Eu003Cpreu003Eu002F**u003Cbru003E * Performs the redirect (or forward) to the login form URL.u003Cbru003E *u002Fu003Cbru003Epublic void commence(HttpServletRequest request, HttpServletResponse response,u003Cbru003EttAuthenticationException authException) {u003Cbru003EtString redirectUrl = null;u003Cbru003Etif (useForward) {u003Cbru003Ettif (forceHttps && “http”.equals(request.getScheme())) {u003Cbru003EtttredirectUrl = buildHttpsRedirectUrlForRequest(request);u003Cbru003Ett}u003Cbru003Ettif (redirectUrl == null) {u003Cbru003EtttString loginForm = determineUrlToUseForThisRequest(request, response,u003Cbru003EtttttauthException);u003Cbru003Etttif (logger.isDebugEnabled()) {u003Cbru003Ettttlogger.debug(“Server side forward to: ” + loginForm);u003Cbru003Ettt}u003Cbru003EtttRequestDispatcher dispatcher = request.getRequestDispatcher(loginForm);u003Cbru003Etttdispatcher.forward(request, response);u003Cbru003Etttreturn;u003Cbru003Ett}u003Cbru003Et}u003Cbru003Etelse {u003Cbru003EttredirectUrl = buildRedirectUrlToLoginPage(request, response, authException);u003Cbru003Et}u003Cbru003EtredirectStrategy.sendRedirect(request, response, redirectUrl);u003Cbru003E}u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E首先我们从这个方法的注释中就可以看出,这个方法是用来决定到底是要重定向还是要 forward,通过 Debug 追踪,我们发现默认情况下 useForward 的值为 false,所以请求走进了重定向。u003Cu002Fpu003Eu003Cpu003E那么我们解决问题的思路很简单,直接重写这个方法,在方法中返回 JSON 即可,不再做重定向操作,具体配置如下:u003Cu002Fpu003Eu003Cpreu003E@Overrideu003Cbru003Eprotected void configure(HttpSecurity http) throws Exception {u003Cbru003E http.authorizeRequests()u003Cbru003E .anyRequest().authenticated()u003Cbru003E .formLogin()u003Cbru003E .loginProcessingUrl(“u002FdoLogin”)u003Cbru003E .loginPage(“u002Flogin”)u003Cbru003E u002Fu002F其他配置u003Cbru003E .permitAll()u003Cbru003E .and()u003Cbru003E .csrf().disable().exceptionHandling()u003Cbru003E .authenticationEntryPoint(new AuthenticationEntryPoint() {u003Cbru003E @Overrideu003Cbru003E public void commence(HttpServletRequest req, HttpServletResponse resp, AuthenticationException authException) throws IOException, ServletException {u003Cbru003E resp.setContentType(“applicationu002Fjson;charset=utf-8”);u003Cbru003E PrintWriter out = resp.getWriter();u003Cbru003E RespBean respBean = RespBean.error(“访问失败!”);u003Cbru003E if (authException instanceof InsufficientAuthenticationException) {u003Cbru003E respBean.setMsg(“请求失败,请联系管理员!”);u003Cbru003E }u003Cbru003E out.write(new ObjectMapper().writeValueAsString(respBean));u003Cbru003E out.flush();u003Cbru003E out.close();u003Cbru003E }u003Cbru003E });u003Cbru003E}u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E在 Spring Security 的配置中加上自定义的 AuthenticationEntryPoint 处理方法,该方法中直接返回相应的 JSON 提示即可。这样,如果用户再去直接访问一个需要认证之后才可以访问的请求,就不会发生重定向操作了,服务端会直接给浏览器一个 JSON 提示,浏览器收到 JSON 之后,该干嘛干嘛。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E结语u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E好了,一个小小的重定向问题和小伙伴们分享下,不知道大家有没有看懂呢?u003Cu002Fpu003Eu003Cu002Fdivu003E”

原文始发于:Spring Security 前后端分离登录,非法请求直接返回 JSON

主题测试文章,只做测试使用。发布者:逗乐男神i,转转请注明出处:http://www.cxybcw.com/26510.html

联系我们

13687733322

在线咨询:点击这里给我发消息

邮件:1877088071@qq.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code