springsecurity实现手机验证码登录的方法
时间: 2023-08-06 19:12:29 浏览: 52
Spring Security提供了一个非常灵活的身份验证框架,可以轻松地实现各种类型的身份验证,包括手机验证码登录。下面是实现手机验证码登录的步骤:
1. 配置SecurityConfig
在SecurityConfig类中添加配置,指定手机验证码登录的URL、登录成功和失败的处理逻辑等。
```
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService userDetailsService;
@Autowired
private MyAuthenticationSuccessHandler authenticationSuccessHandler;
@Autowired
private MyAuthenticationFailureHandler authenticationFailureHandler;
@Autowired
private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.apply(smsCodeAuthenticationSecurityConfig)
.and()
.authorizeRequests()
.antMatchers("/sms/code").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login")
.successHandler(authenticationSuccessHandler)
.failureHandler(authenticationFailureHandler)
.permitAll()
.and()
.logout()
.permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
}
```
2. 自定义身份验证过滤器
创建一个自定义的身份验证过滤器,用于验证用户输入的验证码是否正确。
```
public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private String mobileParameter = "mobile";
private String smsCodeParameter = "smsCode";
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 smsCode = obtainSmsCode(request);
if (StringUtils.isBlank(mobile) || StringUtils.isBlank(smsCode)) {
throw new AuthenticationServiceException("Mobile and SmsCode must not be empty");
}
SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile, smsCode);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
protected String obtainMobile(HttpServletRequest request) {
return request.getParameter(mobileParameter);
}
protected String obtainSmsCode(HttpServletRequest request) {
return request.getParameter(smsCodeParameter);
}
protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
public void setMobileParameter(String mobileParameter) {
this.mobileParameter = mobileParameter;
}
public void setSmsCodeParameter(String smsCodeParameter) {
this.smsCodeParameter = smsCodeParameter;
}
public void setPostOnly(boolean postOnly) {
this.postOnly = postOnly;
}
}
```
3. 自定义身份验证Token
创建一个自定义的身份验证Token,用于存储用户输入的手机号码和验证码。
```
public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = 420L;
private final Object principal;
private Object credentials;
public SmsCodeAuthenticationToken(String mobile, String smsCode) {
super(null);
this.principal = mobile;
this.credentials = smsCode;
setAuthenticated(false);
}
public SmsCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = null;
super.setAuthenticated(true);
}
@Override
public Object getCredentials() {
return credentials;
}
@Override
public Object getPrincipal() {
return principal;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
if (isAuthenticated) {
throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
}
super.setAuthenticated(false);
}
}
```
4. 自定义身份验证提供者
创建一个自定义的身份验证提供者,用于根据手机号码和验证码验证用户身份。
```
public class SmsCodeAuthenticationProvider implements AuthenticationProvider {
@Autowired
private CustomUserDetailsService userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication;
String mobile = authenticationToken.getPrincipal().toString();
String smsCode = authenticationToken.getCredentials().toString();
UserDetails userDetails = userDetailsService.loadUserByMobile(mobile);
if (userDetails == null) {
throw new UsernameNotFoundException("User not found by mobile: " + mobile);
}
// 验证短信验证码
if (!"123456".equals(smsCode)) {
throw new BadCredentialsException("Invalid sms code");
}
SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(userDetails, userDetails.getAuthorities());
authenticationResult.setDetails(authenticationToken.getDetails());
return authenticationResult;
}
@Override
public boolean supports(Class<?> authentication) {
return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
}
}
```
5. 配置身份验证安全
创建一个配置类,用于添加自定义的身份验证过滤器和身份验证提供者。
```
@Configuration
public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
@Autowired
private UserDetailsService userDetailsService;
@Override
public void configure(HttpSecurity http) throws Exception {
SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();
smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
smsCodeAuthenticationFilter.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler("/login?error"));
smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(new SavedRequestAwareAuthenticationSuccessHandler());
SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();
smsCodeAuthenticationProvider.setUserDetailsService(userDetailsService);
http.authenticationProvider(smsCodeAuthenticationProvider)
.addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
}
```
6. 自定义用户详情服务
创建一个自定义的用户详情服务,用于根据手机号码获取用户信息。
```
@Service("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return null;
}
public UserDetails loadUserByMobile(String mobile) throws UsernameNotFoundException {
// TODO: 根据手机号码获取用户信息
return null;
}
}
```
7. 添加验证码生成和发送逻辑
在登录页面中添加生成和发送验证码的逻辑,当用户点击“发送验证码”按钮时,调用后台接口生成验证码并发送到用户手机上。
```
@PostMapping("/sms/code")
@ResponseBody
public ResponseEntity<Object> createSmsCode(@RequestParam("mobile") String mobile) {
// TODO: 生成验证码并发送到用户手机上
return ResponseEntity.ok().build();
}
```
最后,启动应用程序,访问登录页面,输入手机号码和验证码即可完成登录。