Spring Security 做前后端分离的数据交互

在前后端分离项目中,项目的交互都是通过 JSON 来进行,无论登录成功还是失败,后端只需返回是否成功的JSON给前端,由前端决定页面的跳转问题,和后端没有关系了。

前面两章我们已经简单的使用了Spring Security做表单跳转,前后端分离需要用到successHandler来配置登录成功的回调。
前两章传送门
Spring Security初使用
Spring Security自定义表单登录详解

successHandler 的功能十分强大,甚至已经囊括了 defaultSuccessUrl 和 successForwardUrl 的功能。我们来看一下:

.successHandler((req, resp, authentication) -> {
    Object principal = authentication.getPrincipal();
    resp.setContentType("application/json;charset=utf-8");
    PrintWriter out = resp.getWriter();
    out.write(new ObjectMapper().writeValueAsString(principal));
    out.flush();
    out.close();
})

successHandler 方法的参数是一个 AuthenticationSuccessHandler 对象,这个对象中我们要实现的方法是 onAuthenticationSuccess。
onAuthenticationSuccess 方法有三个参数,分别是:

  • HttpServletRequest
  • HttpServletResponse
  • Authentication

利用 HttpServletRequest 我们可以做服务端跳转,利用 HttpServletResponse 我们可以做客户端跳转,当然,也可以返回 JSON 数据。
第三个 Authentication 参数则保存了我们刚刚登录成功的用户信息。

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .anyRequest().authenticated()
            .and()//结束当前标签,上下文回到HttpSecurity,开启新一轮的配置。
            .formLogin()
            .loginPage("/login.html")//登录页
            .loginProcessingUrl("/doLogin")
            .successHandler((req, resp, authentication) -> {
                Object principal = authentication.getPrincipal();
                resp.setContentType("application/json;charset=utf-8");
                PrintWriter out = resp.getWriter();
                out.write(new ObjectMapper().writeValueAsString(principal));
                out.flush();
                out.close();
            })
            .permitAll()//登录相关的页面/接口不拦截。
            .and()
            .csrf().disable();
}

配置如上信息,查看全部配置请看我前两章Spring Security文章
启动项目,请求登录接口
在这里插入图片描述
登录失败也有一个类似的回调,如下:

.failureHandler((req, resp, e) -> {
    resp.setContentType("application/json;charset=utf-8");
    PrintWriter out = resp.getWriter();
    out.write(e.getMessage());
    out.flush();
    out.close();
})

失败的回调也是三个参数,第三个是一个 Exception保存了登录失败的原因,我们可以将之通过 JSON 返回到前端。也可以去识别异常的类型,根据不同的异常类型,返回对应的错误信息。

未认证访问一个需要认证后才能访问的页面,系统默认重定向到登录页面,但是在前后端分离中,应该给用户一个尚未登录的提示,前端收到提示之后,再自行决定页面跳转。
具体配置如下:

.csrf().disable().exceptionHandling()
.authenticationEntryPoint((req, resp, authException) -> {
            resp.setContentType("application/json;charset=utf-8");
            PrintWriter out = resp.getWriter();
            out.write("尚未登录,请先登录");
            out.flush();
            out.close();
        }
);

重启项目,访问http://localhost:8080/hello
在这里插入图片描述
最后还有注销登录,前后端分离项目,注销登录成功后返回 JSON 即可,配置如下:

.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessHandler((req, resp, authentication) -> {
    resp.setContentType("application/json;charset=utf-8");
    PrintWriter out = resp.getWriter();
    out.write("注销成功");
    out.flush();
    out.close();
})
.permitAll()
.and()

在这里插入图片描述
最后附上configure完整的配置

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .anyRequest().authenticated()
            .and()//结束当前标签,上下文回到HttpSecurity,开启新一轮的配置。
            .formLogin()
            .loginPage("/login.html")//登录页
            .loginProcessingUrl("/doLogin")
            .successHandler((req, resp, authentication) -> {
                Object principal = authentication.getPrincipal();
                resp.setContentType("application/json;charset=utf-8");
                PrintWriter out = resp.getWriter();
                out.write(new ObjectMapper().writeValueAsString(principal));
                out.flush();
                out.close();
            })
            .failureHandler((req, resp, e) -> {
                resp.setContentType("application/json;charset=utf-8");
                PrintWriter out = resp.getWriter();
                out.write(e.getMessage());
                out.flush();
                out.close();
            })
            .and()
            .logout()
            .logoutUrl("/logout")
            .logoutSuccessHandler((req, resp, authentication) -> {
                resp.setContentType("application/json;charset=utf-8");
                PrintWriter out = resp.getWriter();
                out.write("注销成功");
                out.flush();
                out.close();
            })
            .permitAll()//登录相关的页面/接口不拦截。
            .and()
            .csrf().disable().exceptionHandling()
            .authenticationEntryPoint((req, resp, authException) -> {
                        resp.setContentType("application/json;charset=utf-8");
                        PrintWriter out = resp.getWriter();
                        out.write("尚未登录,请先登录");
                        out.flush();
                        out.close();
                    }
            );
}

好了,本文就介绍下前后端分离中常见的 JSON 交互问题。

猜你喜欢

转载自blog.csdn.net/qq_40548741/article/details/107667547