文章目录
- SpringSecurity:登录
- 一、登录处理器
- 1.1 自定义AuthenticationSuccessHandler
- 1.2 自定义AuthenticationFailureHandler
- 1.3 DelegatingAuthenticationFailureHandler
- 二、设置登录成功页和失败页
- 2.1 默认情况下
- 2.2 指定URL处理
- 2.3 自定义认证成功、失败处理
- CustomAuthenticationSuccessHandler:
- CustomAuthenticationFailureHandler:
- 修改 WebSecurityConfig:
- 测试
- 三、认证入口
- 3.1 AuthenticationEntryPoint的使用
SpringSecurity:登录
一、登录处理器
Security默认提供的处理器处理的,一般多用于前后端不分离。
Spring Security
的AuthenticationManager
用来处理身份认证的请求,处理的结果分两种:
- 认证成功:结果由
AuthenticationSuccessHandler
处理 - 认证失败:结果由
AuthenticationFailureHandler
处理
Spring Security
提供了多个实现于AuthenticationSuccessHandler
接口和CustomAuthenticationFailHandler
接口的子类,想自定义处理器,可以实现接口,或继承接口的实现类来重写。
1.1 自定义AuthenticationSuccessHandler
AuthenticationSuccessHandler
是身份验证成功处理器的接口,其下有多个子类:
SavedRequestAwareAuthenticationSuccessHandler
:默认的成功处理器,默认验证成功后,跳转到原路径。也可通过defaultSuccessUrl()
配置。SimpleUrlAuthenticationSuccessHandler
:SavedRequestAwareAuthenticationSuccessHandler
的父类,只有指定defaultSuccessUrl()
时,才会被调用。作用:清除原路径,使用defaultSuccessUrl()
指定的路径。如果直接使用该处理器,则总跳转到根路径。ForwardAuthenticationSuccessHandler
:请求重定向。只有指定successForwardUrl
时被用到。
要想自定义成功处理器,可以通过实现AuthenticationSuccessHandler
接口或继承其子类SavedRequestAwareAuthenticationSuccessHandler
来实现:
实现
AuthenticationSuccessHandler
接口
如果直接返回Json数据时,可以实现AuthenticationSuccessHandler
接口:
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler{
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication)
throws ServletException, IOException {
response.setContentType("application/json;charset=UTF-8");
response.getWriter().append(
new ObjectMapper().createObjectNode()
.put("status", 200)
.put("msg", "登录成功")
.toString());
}
}
继承
SavedRequestAwareAuthenticationSuccessHandler
类
如果只是在登录认证后,需要处理数据,再跳转回原路径时,可以继承该类:
public class CustomAuthenticationSuccessHandler2 extends SavedRequestAwareAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication)
throws ServletException, IOException {
// 登录成功后,进行数据处理
System.out.println("用户登录成功啦!!!");
String authenticationStr = objectMapper.writeValueAsString(authentication);
System.out.println("用户登录信息打印:" + authenticationStr);
//处理完成后,跳转回原请求URL
super.onAuthenticationSuccess(request, response, authentication);
}
}
Spring Security
默认是使用SavedRequestAwareAuthenticationSuccessHandler
,在配置中修改为自定义的AuthenticationSuccessHandler
:
1.2 自定义AuthenticationFailureHandler
AuthenticationFailureHandler
是身份认证失败处理器的接口,其下有多个子类实现:
-
SimpleUrlAuthenticationFailureHandler
:默认的失败处理器,默认认证失败后,跳转到登录页路径加error
参数,如:http://localhost:8080/login?error
。可通过failureUrl()
配置 -
ForwardAuthenticationFailureHandler
:重定向到指定的URL -
DelegatingAuthenticationFailureHandler
:将AuthenticationException
子类委托给不同的AuthenticationFailureHandler
,意味着可以为AuthenticationException
的不同实例创建不同的行为 -
ExceptionMappingAuthenticationFailureHandler
:可以根据不同的AuthenticationException
类型,设置不同的跳转url
自定义失败处理器,可以通过实现AuthenticationFailureHandler
接口或继承其子类SimpleUrlAuthenticationFailureHandler
来实现:
实现
AuthenticationFailureHandler
接口:
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception)
throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
response.getWriter().append(
new ObjectMapper().createObjectNode()
.put("status", 401)
.put("msg", "用户名或密码错误")
.toString());
}
}
继承
SimpleUrlAuthenticationFailureHandler
类
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception)
throws IOException, ServletException {
// 登录失败后,进行数据处理
System.out.println("登录失败啦!!!");
String exceptionStr = objectMapper.writeValueAsString(exception.getMessage());
System.out.println(exceptionStr);
// 跳转原页面
super.onAuthenticationFailure(request, response, exception);
}
}
Spring Security
默认验证失败是使用**SimpleUrlAuthenticationFailureHandler
**,在配置中修改为自定义的AuthenticationFailureHandler
:
1.3 DelegatingAuthenticationFailureHandler
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
@Bean
public DelegatingAuthenticationFailureHandler delegatingAuthenticationFailureHandler(){
LinkedHashMap<Class<? extends AuthenticationException>, AuthenticationFailureHandler> handlers = new LinkedHashMap<>();
// 登录失败时,使用的失败处理器
handlers.put(BadCredentialsException.class, new BadCredentialsAuthenticationFailureHandler());
// 用户过期时,使用的失败处理器
handlers.put(AccountExpiredException.class, new AccountExpiredAuthenticationFailureHandler());
// 用户被锁定时,使用的失败处理
handlers.put(LockedException.class, new LockedAuthenticationFailureHandler());
return new DelegatingAuthenticationFailureHandler(handlers, new AuthenticationFailureHandler());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
// 配置使用自定义失败处理器
.failureHandler(delegatingAuthenticationFailureHandler());
}
}
1.4 ExceptionMappingAuthenticationFailureHandler
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
@Bean
public ExceptionMappingAuthenticationFailureHandler exceptionMappingAuthenticationFailureHandler(){
ExceptionMappingAuthenticationFailureHandler handler = new ExceptionMappingAuthenticationFailureHandler();
HashMap<String, String> map = new HashMap<>();
// 登录失败时,跳转到 /badCredentials
map.put(BadCredentialsException.class.getName(), "/badCredentials");
// 用户过期时,跳转到 /accountExpired
map.put(AccountExpiredException.class.getName(), "/accountExpired");
// 用户被锁定时,跳转到 /locked
map.put(LockedException.class.getName(), "/locked");
handler.setExceptionMappings(map);
return handler;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
// 配置使用自定义失败处理器
.failureHandler(exceptionMappingAuthenticationFailureHandler());
}
}
二、设置登录成功页和失败页
2.1 默认情况下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LYZrJm2D-1638256792397)(SpringSecurity:登录.assets/image-20211130142459058.png)]
当我们输入用户名或者密码错误的时候,页面不会显示错误信息,控制台也不报错
这是因为首先 /login?error
是 Spring security 默认的失败 Url,其次如果你不手动处理这个异常,这个异常是不会被处理的。
2.2 指定URL处理
指定错误Url,WebSecurityConfig
中添加.failureUrl("/login/error")
//设置登录操作
http.formLogin()
//设置登陆页
.loginPage("/login")
.defaultSuccessUrl("/")
//设置登录失败页
.failureUrl("/login/error")
//设置登录成功页
.defaultSuccessUrl("/")
.permitAll();
在Controller中处理异常
@RequestMapping("/login/error")
public void loginError(HttpServletRequest request, HttpServletResponse response) {
response.setContentType("text/html;charset=utf-8");
AuthenticationException exception =
(AuthenticationException)request.getSession().getAttribute("SPRING_SECURITY_LAST_EXCEPTION");
try {
response.getWriter().write(exception.toString());
}catch (IOException e) {
e.printStackTrace();
}
}
测试结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jqi0niNw-1638256792399)(SpringSecurity:登录.assets/image-20211130142941090.png)]
2.3 自定义认证成功、失败处理
关于成功和失败的处理,前面我们是用failureUrl()
来指定失败后的URL,defaultSuccessUrl()
指定认证成功后URL,我们可以通过设置 successHandler()
和 failureHandler()
来实现自定义认证成功、失败处理。
CustomAuthenticationSuccessHandler:
@Component
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
logger.info("登录成功,{}", authentication);
response.sendRedirect("/");
}
}
onAuthenticationSuccess()
方法的第三个参数 Authentication
为认证后该用户的认证信息,这里打印日志后,重定向到了首页。
CustomAuthenticationFailureHandler:
@Component
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Autowired
private ObjectMapper objectMapper;
private final Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
logger.info("登陆失败");
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(exception.getMessage()));
}
}
onAuthenticationFailure()
方法的第三个参数 exception
为认证失败所产生的异常,这里也是简单的返回到前台。
修改 WebSecurityConfig:
首先需要把我们自定义的类注入进来
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kUPN4p8O-1638256792400)(SpringSecurity:登录.assets/image-20211130143606069.png)]
注意:
这俩种方式只能存在一种
测试
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M4nt9jfs-1638256792401)(SpringSecurity:登录.assets/image-20211130143822194.png)]
三、认证入口
AuthenticationEntryPoint
是Spring Security
认证入口点接口,在用户请求处理过程中遇到认证异常时,使用特定认证方式进行认证。
AuthenticationEntryPoint
内置实现类:
-
LoginUrlAuthenticationEntryPoint
:根据配置的登录页面url
,将用户重定向到该登录页面进行认证。默认的认证方式。 -
Http403ForbiddenEntryPoint
:设置响应状态为403
,不触发认证。通常在预身份认证中设置 -
HttpStatusEntryPoint
:设置特定的响应状态码,不触发认证。 -
BasicAuthenticationEntryPoint
:设置基本(Http Basic
)认证,在响应状态码401
和Header
为WWW-Authenticate:"Basic realm="xxx"
时使用。 -
DigestAuthenticationEntryPoint
:设置摘要(Http Digest
)认证,在响应状态码401
和Header
为WWW-Authenticate:"Digest realm="xxx"
时使用。 -
DelegatingAuthenticationEntryPoint
:根据匹配URI来委托给不同的AuthenticationEntryPoint
,且必须制定一个默认的认证方式。
3.1 AuthenticationEntryPoint的使用
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
@Bean
public DelegatingAuthenticationEntryPoint delegatingAuthenticationEntryPoint() {
LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> map = new LinkedHashMap<>();
// GET方式请求/test时,直接返回 403
map.put(new AntPathRequestMatcher("/test", "GET"), new Http403ForbiddenEntryPoint());
// 访问 /basic时,直接返回 400 bad request
map.put(new AntPathRequestMatcher("/basic"),
new HttpStatusEntryPoint(HttpStatus.BAD_REQUEST));
DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint(map);
// 除了上面两个 uri 配置指定的认证入口,其它默认使用 LoginUrlAuthenticationEntryPoint认证入口
entryPoint.setDefaultEntryPoint(new LoginUrlAuthenticationEntryPoint("/user-login"));
return entryPoint;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
/**
* Http403ForbiddenEntryPoint 用法
*/
// http.exceptionHandling()
// .authenticationEntryPoint(new Http403ForbiddenEntryPoint());
/**
* HttpStatusEntryPoint 用法
*/
// http.exceptionHandling()
// .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.BAD_REQUEST));
/**
* DelegatingAuthenticationEntryPoint 用法
*/
http.exceptionHandling()
.authenticationEntryPoint(delegatingAuthenticationEntryPoint());
...
}
}