下面我将详细讲解如何使用Spring Security实现短信验证码功能。这里假设你已经有了一个基于Spring Security的Web应用程序,现在要添加短信验证码功能。
准备工作
在开始实现之前需要进行一些准备工作:
1.添加Spring Security支持短信验证码功能的依赖;
在pom.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>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-servlet</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2.配置短信验证码发送的相关信息;
可以使用Spring的@Configuration
注解创建一个Bean来配置短信验证码发送的相关信息,例如短信网关地址、短信模板等。
实现
下面将分步骤来实现Spring Security中的短信验证码功能。
步骤1:创建一个用于发送短信验证码的服务
可以使用Spring的@Component
注解创建一个短信发送服务的Bean。
@Component
public class SmsCodeSender {
@Autowired
private SmsCodeProperties smsCodeProperties;
private final static Logger logger = LoggerFactory.getLogger(SmsCodeSender.class);
public void sendSmsCode(String mobile, String code) {
// 发送短信验证码的代码
}
}
步骤2:编写一个生成短信验证码的过滤器
可以使用Spring的OncePerRequestFilter
来过滤需要生成短信验证码的请求。
public class SmsCodeFilter extends OncePerRequestFilter {
@Autowired
private SmsCodeSender smsCodeSender;
@Autowired
private SmsCodeProperties smsCodeProperties;
private AntPathMatcher pathMatcher = new AntPathMatcher();
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if ("POST".equalsIgnoreCase(request.getMethod()) && pathMatcher.match("/login/mobile", request.getServletPath())) {
try {
validate(new ServletWebRequest(request));
} catch (ValidationException e) {
// 验证失败,返回错误信息
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("验证码错误或已过期!");
}
} else {
filterChain.doFilter(request, response);
}
}
private void validate(ServletWebRequest request) {
String mobile = request.getParameter("mobile");
String smsCode = request.getParameter("smsCode");
// 从Session中获取短信验证码
String codeInSession = (String) request.getRequest().getSession().getAttribute(SmsCodeController.SESSION_KEY_SMS_CODE + mobile);
if (StringUtils.isBlank(smsCode)) {
throw new ValidationException("验证码不能为空!");
}
if (codeInSession == null) {
throw new ValidationException("验证码不存在!");
}
if (!StringUtils.equals(codeInSession, smsCode)) {
throw new ValidationException("验证码不正确!");
}
request.getRequest().getSession().removeAttribute(SmsCodeController.SESSION_KEY_SMS_CODE + mobile);
}
}
步骤3:配置短信验证码过滤器
需要在Spring Security过滤器链中添加自定义的短信验证码过滤器,以便拦截需要验证短信验证码的请求。
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private SmsCodeFilter smsCodeFilter;
// ...
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(smsCodeFilter, UsernamePasswordAuthenticationFilter.class)
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login")
.successForwardUrl("/index")
.and()
.authorizeRequests().antMatchers("/login/**").permitAll()
.anyRequest().authenticated()
.and().csrf().disable();
}
}
步骤4:生成和发送短信验证码
可以使用Spring的@Controller
注解创建一个控制器,在控制器中生成和发送短信验证码。
@Controller
@Slf4j
public class SmsCodeController {
public static final String SESSION_KEY_SMS_CODE = "SESSION_KEY_SMS_CODE:";
@Autowired
private SmsCodeSender smsCodeSender;
@Autowired
private SmsCodeProperties smsCodeProperties;
@PostMapping("/code/sms")
@ResponseBody
public JsonResponse createSmsCode(HttpServletRequest request, @RequestParam("mobile") String mobile) throws ServletRequestBindingException {
if (!MobileUtils.isMobile(mobile)) {
return JsonResponse.error("手机号码格式不正确!");
}
// 生成短信验证码
String code = RandomStringUtils.randomNumeric(6);
String key = SESSION_KEY_SMS_CODE + mobile;
request.getSession().setAttribute(key, code);
request.getSession().setMaxInactiveInterval(smsCodeProperties.getExpireTime());
// 发送短信验证码
smsCodeSender.sendSmsCode(mobile, code);
log.info("手机号 [" + mobile + "] 短信验证码 [" + code + "] 已发送");
return JsonResponse.ok("短信验证码已发送!");
}
}
示例1
这个示例的目的是显示一个用于获取验证码的按钮,并添加一个JavaScript函数来调用后端的短信列表。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<form id="login-form" action="/login" method="post" style="padding: 10px;">
<input type="text" name="mobile" placeholder="电话号码"/>
<input type="text" name="smsCode" placeholder="短信验证码"/>
<button id="sms-button" type="button">获取短信验证码</button>
<button type="submit">登录</button>
</form>
<script>
$(document).ready(function () {
$('#sms-button').click(function () {
$.ajax({
url: '/code/sms',
data: {
mobile: $('input[name="mobile"]').val()
},
success: function (data) {
alert(data.message);
},
error: function (e) {
alert("发生错误!");
}
});
});
});
</script>
</body>
</html>
在上面的代码中,#sms-button
是用于获取验证码的按钮的ID。当用户单击该按钮时,JavaScript将会以GET方法调用/code/sms
地址。后端的SmsCodeController
将会生成和发送短信验证码。
示例2
一个简单的启用短信验证码功能的Spring Security配置可以是这样的:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private SmsCodeFilter smsCodeFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(smsCodeFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/", "/login**").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login")
.successHandler(new LoginSuccessHandler())
.failureHandler(new LoginFailureHandler())
.and()
.logout()
.logoutUrl("/logout")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
.permitAll()
.and()
.csrf().disable();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
在上面的示例代码中,除了添加SmsCodeFilter
过滤器之外,其他内容都和普通的Spring Security配置一样。例如,/admin/**
路径要求用户有ADMIN
角色,所有其他请求都需要用户被验证(即登录)。LoginSuccessHandler
和LoginFailureHandler
是登录成功或失败时将要执行的一些逻辑。关闭CSRF保护是为了使示例变得更加简单,可以在实际项目中开启它。
结论
Spring Security提供了一个灵活和可扩展的框架,可以轻松实现短信验证码功能。使用短信验证码可以提高Web应用程序的安全性,同时也为用户提供更加便捷的登录方式。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringSceurity实现短信验证码功能的示例代码 - Python技术站