一.简介
在前后端分离这样的开发模式下,前后端的交互都是通过 JSON 来进行数据传递的,无论登录成功还是失败,都不会有服务端跳转或者客户端跳转之类的操作。
也就是说无论登录成功还是失败,服务端都会返回一段登录成功或失败的 JSON 信息给前端,前端收到JSON之后来决定是该跳转到成功界面还是失败界面,和后端没有关系。
二.认证处理时的相关API
2.1页面跳转的相关API
2.1.1登录成功时的跳转API
表单认证时,处理登录成功时,跳转到某个页面的API是如下两个方法:
- defaultSuccessUrl
- successForwardUrl
以上两个方法都是用来配置跳转地址的,适用于前后端不分离时的开发。
2.1.2登录失败时的跳转API
处理登录失败时,跳转页面的API是如下两个方法:
- failureUrl()
- failureForwardUrl()
以上两个方法也是用来配置跳转地址的,同样适用于前后端不分离时的开发。
2.2返回JSON格式的处理器
上面的两类方法,无论是认证成功还是认证失败,都是在前后端不分离时的处理方案,直接从Java后端跳转到某个页面上。那么在前后端分离时,Java后端项目中,根本就没有页面。
在前后端分离模式下,既然后端没有页面,页面都在前端,那就可以考虑使用JSON来进行信息交互了,我们把认证成功或认证失败的信息,以JSON的格式传递给前端,由前端来决定到底该往哪个页面跳转就好了。
如果要返回JSON格式的信息,有如下相关方法:
- successHandler()
- failureHandler()
- logoutSuccessHandler()
- authenticationEntryPoint()
三. 创建SpringSecurity项目
参考之前的文章,这边不做叙述。
四. 认证成功时的处理方案
首先看下相关的方法及其核心参数,即successHandler()和onAuthenticationSuccess参数。
4.1successHandler()方法
**successHandler()**方法的功能十分强大,囊括了 defaultSuccessUrl()和 successForwardUrl() 的功能。
**successHandler()**方法的参数是一个 AuthenticationSuccessHandler 对象,这个对象中我们要实现的方法是 onAuthenticationSuccess()。
4.2onAuthenticationSuccess参数
onAuthenticationSuccess() 方法中有三个参数,分别是:
- HttpServletRequest: 利用该参数我们可以实现服务端的跳转;
- HttpServletResponse: 利用该参数我们可以做客户端的跳转,也可以返回 JSON 数据;
- Authentication: 这个参数则保存了我们刚刚登录成功的用户信息。
4.3定义SecurityAuthenticationSuccessHandler类
了解完主要的方法和参数之后,我们先来编写一个处理器类,该类需要实现SavedRequestAwareAuthenticationSuccessHandler接口。
代码如下:
/**
* 处理登录成功时的业务逻辑
*/
public class SecurityAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
/**
* Authentication:携带登录的用户名及角色等信息
*/
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
//直接输出json格式的响应信息
Object principal = authentication.getPrincipal();
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
//以json格式对外输出身份信息
out.write(new ObjectMapper().writeValueAsString(principal));
out.flush();
out.close();
}
}
4.4配置successHandler
然后在SecurityConfig配置类中,调用successHandler()方法,把前面定义的SecurityAuthenticationSuccessHandler类关联进来。代码如下:
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.permitAll()
//认证成功时的处理器
.successHandler(new SecurityAuthenticationSuccessHandler())
.and()
.csrf()
.disable();
}
}
4.5验证
配置完成后,进行登录验证,在认证成功后,就可以看到登录成功的用户信息是通过 JSON 返回到前端的,如图所示:
在认证成功后,Spring Security会把认证的用户信息以JSON格式展示出来,比如用户名、密码、角色等信息。
五.认证失败时的处理方案
如何实现认证失败时的处理方案,同样的,先看下相关的API方法及参数。
5.1failureHandler()
failureHandler()方法的参数是一个 AuthenticationFailureHandler 对象,这个对象中我们要实现的方法是 onAuthenticationFailure()。
onAuthenticationFailure()方法有三个参数,分别是:
- HttpServletRequest: 利用该参数我们可以实现服务端的跳转;
- HttpServletResponse: 利用该参数我们可以做客户端的跳转,也可以返回 JSON 数据;
- AuthenticationException: 这个参数则保存了登录失败的原因。
5.2定义SecurityAuthenticationFailureHandler类
SecurityAuthenticationFailureHandler类的代码如下:
/**
* 处理登录失败时的业务逻辑
*/
public class SecurityAuthenticationFailureHandler extends ExceptionMappingAuthenticationFailureHandler {
/**
* AuthenticationException:异常信息
*/
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
//直接输出json格式的响应信息
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(e.getMessage());
out.flush();
out.close();
}
}
5.3配置failureHandler
接着在SecurityConfig配置类中,调用failureHandler()方法来关联上面定义的SecurityAuthenticationFailureHandler类对象。代码如下:
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.permitAll()
//认证成功时的处理器
.successHandler(new SecurityAuthenticationSuccessHandler())
//认证失败时的处理器
.failureHandler(new SecurityAuthenticationFailureHandler())
.and()
.csrf()
.disable();
}
}
5.4验证
配置完成后,再去登录,在认证失败时,就可以看到登录失败的用户信息通过 JSON 返回到前端了,如下图所示:
六.退出登录时的处理方案
实现了认证成功和认证失败后的处理方案后,接下来看下如何处理退出登录。
6.1logoutSuccessHandler()
负责退出登录的方法是logoutSuccessHandler(),这个方法中需要一个参数LogoutSuccessHandler;在LogoutSuccessHandler类中有一个方法 onLogoutSuccess(),该方法中的参数与登录成功时的参数一样。
6.2定义SecurityLogoutSuccessHandler类
SecurityLogoutSuccessHandler类的代码如下:
public class SecurityLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.write("注销成功");
out.flush();
out.close();
}
}
6.3 配置logoutSuccessHandler
在SecurityConfig配置类中,调用logoutSuccessHandler()方法来关联上面定义的SecurityLogoutSuccessHandler对象。代码如下:
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.permitAll()
//认证成功时的处理器
.successHandler(new SecurityAuthenticationSuccessHandler())
//认证失败时的处理器
.failureHandler(new SecurityAuthenticationFailureHandler())
.and()
.logout()
//退出登录时的处理器
.logoutSuccessHandler(new SecurityLogoutSuccessHandler())
.and()
.csrf()
.disable();
}
}
6.4验证
配置完成后,我们去访问/logout接口,退出登录成功,截图如下:
七.未认证时的处理方案
接下来看下如何处理未认证时的方案。
大体思路:
如果用户没有登录,就访问一个需要认证后才能访问的页面。这个时候,我们不应该让用户重定向到登录页面,而是给用户一个尚未登录的提示,前端收到提示之后,再自行决定页面跳转。因为在前后端分离时,后端没有页面,未认证时也没办法直接重定向到登录页面啊!
7.1authenticationEntryPoint()
未认证时,同样有个专门的方法来处理,即authenticationEntryPoint()方法,这个方法中需要一个参数LoginUrlAuthenticationEntryPoint,在LoginUrlAuthenticationEntryPoint类中有一个方法 commence()。
7.2定义SecurityAuthenticationEntryPoint类
SecurityAuthenticationEntryPoint类的代码如下:
public class SecurityAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.write("尚未登录,请先登录");
out.flush();
out.close();
}
}
7.3 配置authenticationEntryPoint
在SecurityConfig配置类中,调用authenticationEntryPoint()方法来关联上面定义的SecurityAuthenticationEntryPoint对象。代码如下:
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.permitAll()
//认证成功时的处理器
.successHandler(new SecurityAuthenticationSuccessHandler())
//认证失败时的处理器
.failureHandler(new SecurityAuthenticationFailureHandler())
.and()
.csrf()
.disable()
.exceptionHandling()
//未登录时的处理器
.authenticationEntryPoint(new SecurityAuthenticationEntryPoint());
}
}
7.4验证
配置完成后,在未登录时,直接去访问项目中的某个接口,就会看到未登录时返回的JSON信息,截图如下: