springsecurity实现图片验证码登录
时间: 2023-05-11 08:06:27 浏览: 77
Spring Security 可以通过自定义 AuthenticationProvider 实现图片验证码登录。具体实现步骤如下:
1. 自定义验证码过滤器,实现验证码的生成和校验逻辑。
2. 自定义 AuthenticationProvider,重写 authenticate 方法,在该方法中校验验证码是否正确。
3. 在 Spring Security 配置文件中配置自定义的验证码过滤器和 AuthenticationProvider。
4. 在登录页面中添加验证码输入框,并在登录请求中提交验证码参数。
5. 在登录请求中添加验证码校验逻辑,如果验证码不正确则返回错误信息。
6. 如果验证码校验通过,则调用 AuthenticationProvider 的 authenticate 方法进行登录验证。
7. 如果登录验证通过,则将用户信息保存到 SecurityContext 中,完成登录过程。
具体实现代码可以参考 Spring Security 官方文档和相关博客文章。
相关问题
springsecurity实现手机验证码登录
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实现手机验证码登录的全部步骤。
springsecurity实现短信验证码登录
Spring Security 支持多种认证方式,其中包括短信验证码登录。下面是一个简单的实现步骤:
1. 添加依赖
在 `pom.xml` 文件中添加以下依赖:
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</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>
```
2. 配置 Security
在 `application.yml` 文件中添加以下配置:
```yaml
spring:
security:
user:
name: admin
password: password
sms:
login:
url: /login/sms
parameter:
phone: phone
code: code
```
其中,`user` 节点用于配置默认的用户名和密码,`sms.login` 节点用于配置短信验证码登录的 URL 和参数名。
3. 实现短信验证码登录逻辑
创建一个实现 `UserDetailsService` 接口的类,用于通过手机号查询用户信息。在该类中实现 `loadUserByUsername` 方法,根据手机号查询用户信息并返回 `UserDetails` 对象。
```java
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserService userService;
@Override
public UserDetails loadUserByUsername(String phone) throws UsernameNotFoundException {
User user = userService.getByPhone(phone);
if (user == null) {
throw new UsernameNotFoundException("手机号不存在");
}
return new User(user.getUsername(), user.getPassword(), user.getAuthorities());
}
}
```
创建一个实现 `AuthenticationProvider` 接口的类,用于处理短信验证码登录。在该类中实现 `authenticate` 方法,根据手机号和验证码验证用户信息并返回 `Authentication` 对象。
```java
@Component
public class SmsAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserService userService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String phone = authentication.getName();
String code = authentication.getCredentials().toString();
User user = userService.getByPhone(phone);
if (user == null) {
throw new UsernameNotFoundException("手机号不存在");
}
// 验证短信验证码
if (!"123456".equals(code)) {
throw new BadCredentialsException("短信验证码错误");
}
return new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
}
@Override
public boolean supports(Class<?> authentication) {
return SmsAuthenticationToken.class.isAssignableFrom(authentication);
}
}
```
其中,`SmsAuthenticationToken` 是自定义的认证对象,用于封装手机号和验证码信息。
4. 配置 Security 登录流程
在 Security 配置类中添加以下配置:
```java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Autowired
private SmsAuthenticationProvider smsAuthenticationProvider;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/login/sms").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().disable()
.logout().disable()
.authenticationProvider(smsAuthenticationProvider)
.addFilterAfter(smsAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
public SmsAuthenticationFilter smsAuthenticationFilter() throws Exception {
SmsAuthenticationFilter filter = new SmsAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
filter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler("/"));
filter.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler("/login?error"));
return filter;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
auth.authenticationProvider(smsAuthenticationProvider);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
```
其中,`SmsAuthenticationFilter` 是自定义的过滤器,用于处理短信验证码登录请求。在该过滤器中,获取手机号和验证码信息,封装为 `SmsAuthenticationToken` 对象并调用 `AuthenticationManager` 进行认证。
```java
public class SmsAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private String phoneParameter = "phone";
private String codeParameter = "code";
public SmsAuthenticationFilter() {
super(new AntPathRequestMatcher("/login/sms", "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String phone = obtainPhone(request);
String code = obtainCode(request);
if (phone == null) {
phone = "";
}
if (code == null) {
code = "";
}
phone = phone.trim();
SmsAuthenticationToken authRequest = new SmsAuthenticationToken(phone, code);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
private String obtainPhone(HttpServletRequest request) {
return request.getParameter(phoneParameter);
}
private String obtainCode(HttpServletRequest request) {
return request.getParameter(codeParameter);
}
private void setDetails(HttpServletRequest request, SmsAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
public void setPhoneParameter(String phoneParameter) {
this.phoneParameter = phoneParameter;
}
public void setCodeParameter(String codeParameter) {
this.codeParameter = codeParameter;
}
}
```
至此,短信验证码登录已经实现。在登录页面中,用户输入手机号和验证码后,发送 POST 请求到 `/login/sms`,即可完成短信验证码登录。