springsecurity实现手机验证码登录
时间: 2023-09-29 13:05:36 浏览: 104
Spring Security 实现短信验证码登录功能
Spring Security可以很方便地实现手机验证码登录,步骤如下:
1. 添加spring-security-web和spring-security-config依赖。
```xml
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring-security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring-security.version}</version>
</dependency>
```
2. 创建一个实现UserDetailsService接口的类,该类用于根据不同的用户名加载用户信息。
```java
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userMapper.selectByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("用户名不存在");
}
return new UserPrincipal(user);
}
}
```
3. 创建一个实现AuthenticationProvider接口的类,该类用于验证用户的手机号和验证码是否正确。
```java
@Service
public class SmsCodeAuthenticationProvider implements AuthenticationProvider {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication;
String mobile = authenticationToken.getPrincipal().toString();
String code = authenticationToken.getCredentials().toString();
String redisCode = redisTemplate.opsForValue().get(SmsCodeAuthenticationFilter.REDIS_SMS_CODE_KEY_PREFIX + mobile);
if (StringUtils.isBlank(redisCode)) {
throw new BadCredentialsException("验证码不存在或已过期");
}
if (!StringUtils.equals(code, redisCode)) {
throw new BadCredentialsException("验证码不正确");
}
UserDetails userDetails = new UserPrincipal(new User(mobile, "", Collections.emptyList()));
return new SmsCodeAuthenticationToken(userDetails, userDetails.getAuthorities());
}
@Override
public boolean supports(Class<?> authentication) {
return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
}
}
```
4. 创建一个实现AuthenticationFilter接口的类,该类用于处理短信验证码登录请求。
```java
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";
public static final String REDIS_SMS_CODE_KEY_PREFIX = "SMS_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/mobile", "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("不支持的请求方式: " + request.getMethod());
}
String mobile = obtainMobile(request);
String code = obtainCode(request);
if (StringUtils.isBlank(mobile)) {
throw new UsernameNotFoundException("手机号不能为空");
}
if (StringUtils.isBlank(code)) {
throw new BadCredentialsException("验证码不能为空");
}
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;
}
}
```
5. 在WebSecurityConfigurerAdapter的子类中进行配置。
```java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Autowired
private SmsCodeAuthenticationProvider smsCodeAuthenticationProvider;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login/mobile").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").defaultSuccessURL("/home").permitAll()
.and()
.logout().logoutUrl("/logout").permitAll()
.and()
.addFilterBefore(smsCodeAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.csrf().disable();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(smsCodeAuthenticationProvider)
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SmsCodeAuthenticationFilter smsCodeAuthenticationFilter() {
SmsCodeAuthenticationFilter filter = new SmsCodeAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager());
filter.setAuthenticationSuccessHandler((request, response, authentication) -> {
response.setContentType("application/json;charset=UTF-8");
PrintWriter out = response.getWriter();
out.write("{\"code\": 0, \"message\": \"登录成功\"}");
out.flush();
out.close();
});
filter.setAuthenticationFailureHandler((request, response, exception) -> {
response.setContentType("application/json;charset=UTF-8");
PrintWriter out = response.getWriter();
out.write("{\"code\": 1, \"message\": \"" + exception.getMessage() + "\"}");
out.flush();
out.close();
});
return filter;
}
}
```
6. 在前端页面中添加短信验证码登录的表单。
```html
<form action="/login/mobile" method="post">
<div>
<label>手机号:</label>
<input type="text" name="mobile" />
</div>
<div>
<label>验证码:</label>
<input type="text" name="code" />
<button type="button" onclick="sendSmsCode()">发送验证码</button>
</div>
<div>
<button type="submit">登录</button>
</div>
</form>
```
7. 在后端控制器中添加发送短信验证码的接口。
```java
@RestController
public class SmsCodeController {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@GetMapping("/sms/code")
public void sendSmsCode(String mobile) {
String code = RandomStringUtils.randomNumeric(6);
redisTemplate.opsForValue().set(SmsCodeAuthenticationFilter.REDIS_SMS_CODE_KEY_PREFIX + mobile, code, 5, TimeUnit.MINUTES);
// 发送短信验证码
}
}
```
以上就是使用Spring Security实现手机验证码登录的全部步骤。
阅读全文