组件Filter、Provider、AuthenticationToken
SmsCodeAuthenticationFilter.java
public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/sms/login",
"POST");
public SmsCodeAuthenticationFilter(AuthenticationManager authenticationManager) {
super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
setAuthenticationManager(authenticationManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
String mobile = request.getParameter("mobile");
String code = request.getParameter("code");
SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile, code);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
}
SmsCodeAuthenticationToken.java
public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {
private String mobile;
private String code;
public SmsCodeAuthenticationToken(String mobile, String code) {
super(null);
this.mobile = mobile;
this.code = code;
setAuthenticated(false);
}
public SmsCodeAuthenticationToken(String mobile, String code,
Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.mobile = mobile;
this.code = code;
super.setAuthenticated(true); // must use super, as we override
}
@Override
public Object getCredentials() {
return this.code;
}
@Override
public Object getPrincipal() {
return this.mobile;
}
}
SmsCodeAuthenticationProvider.java
@AllArgsConstructor
public class SmsCodeAuthenticationProvider implements AuthenticationProvider {
private UserDetailsService userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String mobile = (String)authentication.getPrincipal();
String code = (String) authentication.getCredentials();
// TODO 验证码
validate(mobile, code);
UserDetails userDetails = userDetailsService.loadUserByUsername(mobile);
SmsCodeAuthenticationToken successAuthentication = new SmsCodeAuthenticationToken(userDetails.getUsername(),
userDetails.getPassword(), userDetails.getAuthorities());
return successAuthentication;
}
@Override
public boolean supports(Class<?> authentication) {
return authentication == SmsCodeAuthenticationToken.class;
}
private void validate(String mobile, String code) {
if (!"888888".equals(code)) {
throw new BadCredentialsException("验证码不正确!");
}
}
}
添加到配置
- 添加Provider
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new SmsCodeAuthenticationProvider(username -> new User("jkxyx205", "11", AuthorityUtils.commaSeparatedStringToAuthorityList("USER"))));
}
- 添加Filter
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new SmsCodeAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class);
}
测试页面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
>
<head>
<meta charset="UTF-8">
<title>自定义登录</title>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.0.2/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div style="margin: 40px auto; width: 320px;">
<th:block th:if="${session.SPRING_SECURITY_LAST_EXCEPTION != null}">
<div th:text="${session.SPRING_SECURITY_LAST_EXCEPTION.message}"></div>
</th:block>
<form action="/sms/login" method="post">
<div class="mb-3 row">
<label for="mobile" class="visually-hidden">用户名</label>
<input type="text" class="form-control" id="mobile" name="mobile" value="18898987724">
</div>
<div class="mb-3 row">
<label for="code" class="visually-hidden">手机验证码</label>
<input type="text" class="form-control" id="code" name="code" value="888888">
</div>
<div class="mb-3 row">
<button type="submit" class="btn btn-primary mb-3">登录</button>
</div>
</form>
</div>
</body>
</html>
启动日志
2021-10-10 12:41:07.188 INFO 54574 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@4694f434, org.springframework.security.web.context.SecurityContextPersistenceFilter@ceddaf8, org.springframework.security.web.header.HeaderWriterFilter@5f174dd2, org.springframework.security.web.authentication.logout.LogoutFilter@608b1fd2, com.rick.security.authentication.sms.SmsCodeAuthenticationFilter@56928e17, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@6dc1dc69, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@4833eff3, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@1db87583, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@6993c8df, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@dd2856e, org.springframework.security.web.session.SessionManagementFilter@45aca496, org.springframework.security.web.access.ExceptionTranslationFilter@10a98392, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@70c69586]
SmsCodeAuthenticationFilter
已经添加到了 DefaultSecurityFilterChain
中
Github:https://github.com/jkxyx205/spring-security-learn/tree/smscode-filter