spring security 实现自定义短信验证码登录
时间: 2023-07-11 20:32:15 浏览: 58
要实现自定义短信验证码登录,可以按照以下步骤进行:
1. 添加依赖
在项目中添加 Spring Security 和 Spring Security SMS 模块的依赖。
```
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>com.github.lanceshohara</groupId>
<artifactId>spring-security-sms</artifactId>
<version>1.0.2</version>
</dependency>
```
2. 配置 Spring Security
在 Spring Security 配置文件中添加配置,包括短信验证码登录相关的配置。
```
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login/sms").permitAll()
.anyRequest().authenticated()
.and()
.apply(smsCodeAuthenticationSecurityConfig)
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login/form")
.usernameParameter("username")
.passwordParameter("password")
.defaultSuccessUrl("/")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/")
.permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
```
其中,`SmsCodeAuthenticationSecurityConfig` 是短信验证码登录的相关配置类,需要单独实现。
3. 实现短信验证码登录相关配置
实现 `SmsCodeAuthenticationSecurityConfig` 配置类,其中包括一个短信验证码过滤器和一个短信验证码认证提供者。
```
@Configuration
public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private SmsCodeAuthenticationSuccessHandler smsCodeAuthenticationSuccessHandler;
@Autowired
private SmsCodeAuthenticationFailureHandler smsCodeAuthenticationFailureHandler;
@Override
public void configure(HttpSecurity http) throws Exception {
SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();
smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(smsCodeAuthenticationSuccessHandler);
smsCodeAuthenticationFilter.setAuthenticationFailureHandler(smsCodeAuthenticationFailureHandler);
SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();
smsCodeAuthenticationProvider.setUserDetailsService(userDetailsService);
http.authenticationProvider(smsCodeAuthenticationProvider)
.addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
}
```
其中,`SmsCodeAuthenticationFilter` 是短信验证码过滤器,需要单独实现。`SmsCodeAuthenticationSuccessHandler` 和 `SmsCodeAuthenticationFailureHandler` 分别是短信验证码认证成功和失败的处理器,也需要单独实现。
4. 实现短信验证码过滤器
实现 `SmsCodeAuthenticationFilter` 过滤器,重写 `attemptAuthentication` 方法,来处理短信验证码认证请求。
```
public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public static final String SPRING_SECURITY_FORM_MOBILE_KEY = "mobile";
public static final String SPRING_SECURITY_FORM_CODE_KEY = "code";
private String mobileParameter = SPRING_SECURITY_FORM_MOBILE_KEY;
private String codeParameter = SPRING_SECURITY_FORM_CODE_KEY;
private boolean postOnly = true;
public SmsCodeAuthenticationFilter() {
super(new AntPathRequestMatcher("/login/sms", "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String mobile = obtainMobile(request);
String code = obtainCode(request);
if (mobile == null) {
mobile = "";
}
if (code == null) {
code = "";
}
mobile = mobile.trim();
SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile, code);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
protected String obtainMobile(HttpServletRequest request) {
return request.getParameter(mobileParameter);
}
protected String obtainCode(HttpServletRequest request) {
return request.getParameter(codeParameter);
}
protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
public void setMobileParameter(String mobileParameter) {
this.mobileParameter = mobileParameter;
}
public void setCodeParameter(String codeParameter) {
this.codeParameter = codeParameter;
}
public void setPostOnly(boolean postOnly) {
this.postOnly = postOnly;
}
public final String getMobileParameter() {
return mobileParameter;
}
public final String getCodeParameter() {
return codeParameter;
}
}
```
其中,`SmsCodeAuthenticationToken` 是短信验证码认证的 token 类型,需要单独实现。
5. 实现短信验证码认证提供者
实现 `SmsCodeAuthenticationProvider` 提供者,重写 `authenticate` 方法,来进行短信验证码认证。
```
public class SmsCodeAuthenticationProvider implements AuthenticationProvider {
private UserDetailsService userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication;
UserDetails userDetails = userDetailsService.loadUserByUsername((String) authenticationToken.getPrincipal());
SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());
authenticationResult.setDetails(authenticationToken.getDetails());
return authenticationResult;
}
@Override
public boolean supports(Class<?> authentication) {
return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
}
public UserDetailsService getUserDetailsService() {
return userDetailsService;
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
}
```
6. 实现短信验证码认证成功和失败的处理器
实现 `SmsCodeAuthenticationSuccessHandler` 和 `SmsCodeAuthenticationFailureHandler` 处理器,来处理短信验证码认证成功和失败的情况。
```
public class SmsCodeAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
super.onAuthenticationSuccess(request, response, authentication);
}
}
```
```
public class SmsCodeAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
super.onAuthenticationFailure(request, response, exception);
}
}
```
7. 编写控制器
编写控制器,处理短信验证码登录的请求。
```
@Controller
public class LoginController {
private final static String SMS_LOGIN_PAGE = "sms-login";
@RequestMapping("/login/sms")
public String smsLogin() {
return SMS_LOGIN_PAGE;
}
@RequestMapping(value = "/login/sms", method = RequestMethod.POST)
public void smsLogin(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
String mobile = request.getParameter("mobile");
String code = request.getParameter("code");
SmsCodeAuthenticationToken token = new SmsCodeAuthenticationToken(mobile, code);
AuthenticationManager authenticationManager = new ProviderManager(Collections.singletonList(new SmsCodeAuthenticationProvider()));
Authentication authentication = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
request.getRequestDispatcher("/").forward(request, response);
}
}
```
其中,`SmsCodeAuthenticationToken` 是短信验证码认证的 token 类型,需要单独实现。
以上就是实现自定义短信验证码登录的步骤。