下面我将详细讲解“SpringBoot + SpringSecurity 短信验证码登录功能实现”的完整攻略。
一、准备工作
1. 创建SpringBoot工程
首先,我们需要创建一个SpringBoot工程。可以使用IDEA等常见开发工具,快速创建一个SpringBoot工程。
2. 引入依赖
在pom.xml文件中,我们需要添加如下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
其中,spring-boot-starter-web依赖用于启动Web应用程序,spring-security-web和spring-security-config依赖用于实现安全认证和授权,spring-boot-starter-validation依赖用于数据校验。
3. 配置数据库
此处的数据库可用MySQL等关系型数据库,在项目的application.properties中配置数据源和JPA相关的属性。
4. 创建用户实体类和用户仓库
在Java中,创建用户实体类和用户仓库。具体实现根据业务需求,可自由扩展。
5. 配置短信平台SDK
根据短信平台提供的SDK文档,实现短信验证码的发送功能。具体实现方式可以参考示例代码或者短信平台的官方文档。
二、实现代码
1. 配置SpringSecurity
SpringSecurity提供了一套完整的安全认证和授权框架,我们需要在配置文件中进行相应的配置。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder());
provider.setPreAuthenticationChecks(new UserDetailsChecker());
return provider;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.antMatcher("/**")
.authorizeRequests()
.antMatchers("/login", "/logout").permitAll()
.anyRequest().authenticated()
.and().formLogin()
.loginPage("/login")
.defaultSuccessUrl("/home")
.failureUrl("/login?error=true")
.usernameParameter("username")
.passwordParameter("password")
.and().logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/")
.invalidateHttpSession(true)
.clearAuthentication(true)
.deleteCookies("JSESSIONID");
}
}
这段代码中实现了以下功能:
- 使用BCryptPasswordEncoder进行密码加密
- 自定义了DaoAuthenticationProvider和UserDetailsChecker,达到了更加严格的验证逻辑
- 设置登录成功后的默认页面为"/home"
- 设置登录页面为"/login"
- 配置了退出登录功能
2. 实现短信验证码逻辑
在用户登录时,用户输入手机号码并请求发送短信验证码,短信验证码会发送到用户输入的手机号码上。用户在收到短信后,将短信验证码输入到网站上进行验证,若验证通过,则表示用户登录成功。
@RestController
public class SmsController {
@Autowired
private SmsService smsService;
@GetMapping("/sms/send")
public Long sendSms(String phone) {
Long code = (long) (Math.random() * 1000000);
smsService.sendSms(phone, code.toString());
return code;
}
}
@Service
public class SmsServiceImpl implements SmsService {
@Override
public void sendSms(String phone, String code) {
// 调用短信平台的SDK,发送短信验证码
}
}
以上代码实现了一个发送短信验证码的接口,并使用短信平台的SDK发送短信验证码。在实际开发中,需要结合短信平台提供的文档,进行代码实现。
3. 实现自定义表单登录
除了普通的表单登录外,我们还需要实现短信验证码的表单登录。我们可以通过继承UsernamePasswordAuthenticationFilter实现自定义表单登录。在代码中,我们可以通过手机号码+短信验证码的方式进行认证。
public class SmsAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private static final String SPRING_SECURITY_FORM_PHONE_KEY = "phone";
private static final String SPRING_SECURITY_FORM_SMS_CODE_KEY = "smsCode";
private String phoneParameter = SPRING_SECURITY_FORM_PHONE_KEY;
private String smsCodeParameter = SPRING_SECURITY_FORM_SMS_CODE_KEY;
private boolean postOnly = true;
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String phone = obtainPhone(request);
String smsCode = obtainSmsCode(request);
if (StringUtil.isBlank(phone)) {
throw new UsernameNotFoundException("手机号码不能为空");
}
SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(phone, smsCode);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
private String obtainPhone(HttpServletRequest request) {
return request.getParameter(phoneParameter);
}
private String obtainSmsCode(HttpServletRequest request) {
return request.getParameter(smsCodeParameter);
}
private void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
public void setPhoneParameter(String phoneParameter) {
Assert.hasText(phoneParameter, "Phone parameter must not be empty or null");
this.phoneParameter = phoneParameter;
}
public void setSmsCodeParameter(String smsCodeParameter) {
Assert.hasText(smsCodeParameter, "SMS code parameter must not be empty or null");
this.smsCodeParameter = smsCodeParameter;
}
}
以上代码实现了自定义的表单认证,通过手机号码和短信验证码进行认证。
4. 实现自定义AuthenticationProvider
在代码中,我们还需要实现自定义的AuthenticationProvider,实现对手机号码和短信验证码的认证逻辑。
@Service
public class SmsCodeAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserDetailsService userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication;
UserDetails userDetails = userDetailsService.loadUserByUsername((String) authentication.getPrincipal());
if (userDetails == null) {
throw new UsernameNotFoundException("用户不存在");
}
// 短信验证码验证
checkSmsCode((String) authentication.getCredentials());
SmsCodeAuthenticationToken authResult = new SmsCodeAuthenticationToken(userDetails, userDetails.getAuthorities());
authResult.setDetails(authenticationToken.getDetails());
return authResult;
}
private void checkSmsCode(String smsCode) {
// 验证短信验证码,如果验证失败,则抛出相应的异常
}
@Override
public boolean supports(Class<?> authentication) {
return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
}
}
以上代码实现了对手机号码和短信验证码的认证逻辑。
5. 前端页面
在前端页面中,我们需要为用户提供手机号码的输入框,短信验证码的输入框,以及发送短信验证码的按钮。同时,在表单提交时,需要将相应的数据传递到后台进行验证。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SpringBoot + SpringSecurity 短信验证码登录功能实现</title>
</head>
<body>
<div class="form">
<form action="/login" method="post">
<div>
<label for="phone">手机号码:</label>
<input type="text" name="phone" id="phone"/>
</div>
<div>
<label for="smsCode">短信验证码:</label>
<input type="text" name="smsCode" id="smsCode"/>
<button type="button" id="sendSmsBtn">发送短信验证码</button>
</div>
<div>
<input type="checkbox" name="remember-me" id="remember-me"/>
<label for="remember-me">记住我</label>
</div>
<div>
<input type="submit" value="提交"/>
</div>
</form>
</div>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script>
$(function () {
$("#sendSmsBtn").click(function () {
$.get("/sms/send", {phone: $("#phone").val()}, function (data) {
alert("短信验证码已发送:" + data);
});
});
});
</script>
</body>
</html>
以上代码展示了手机号码和短信验证码的输入框,发送短信验证码的按钮,并通过jQuery实现短信验证码的发送功能。
三、实现示例
为了更好地呈现代码的完整性和可用性,这里提供两个完整的示例代码:
(1)完整示例代码:https://github.com/kaysonli/springboot-security-sms-example
(2)示例2实现不同的发送短信平台基于阿里云短信:https://www.aliyun.com/product/sms
四、总结
通过以上代码实现,我们可以掌握在SpringBoot中,如何使用SpringSecurity实现短信验证码登录功能。在实际开发中,还可以根据业务需求对代码进行相应的扩展和改进,例如引入SpringCloud,结合微服务架构实现多应用授权。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringBoot + SpringSecurity 短信验证码登录功能实现 - Python技术站