您好,登录后才能下订单哦!
在现代Web应用中,用户认证是一个非常重要的功能。Spring Security 是一个功能强大且高度可定制的安全框架,能够为Spring应用提供全面的安全服务。本文将详细介绍如何使用Spring Boot整合Spring Security,实现账号密码和手机验证码两种登录方式。
在开始之前,确保你已经具备以下环境:
首先,使用Spring Initializr创建一个新的Spring Boot项目。选择以下依赖:
生成项目后,导入到你的IDE中。
在pom.xml中添加以下依赖:
<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Spring Boot Starter Security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <!-- Spring Boot Starter Data Redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!-- Thymeleaf -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>
</dependencies>
在application.properties中配置Redis连接信息:
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=
spring.redis.timeout=2000
创建一个配置类SecurityConfig,继承WebSecurityConfigurerAdapter,并重写configure方法:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private SmsCodeAuthenticationProvider smsCodeAuthenticationProvider;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/login", "/sms/login", "/sms/code").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/home")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(smsCodeAuthenticationProvider)
            .userDetailsService(userDetailsService)
            .passwordEncoder(new BCryptPasswordEncoder());
    }
}
创建一个UserDetailsService实现类,用于加载用户信息:
@Service
public class CustomUserDetailsService implements UserDetailsService {
    @Autowired
    private UserRepository userRepository;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("User not found");
        }
        return new org.springframework.security.core.userdetails.User(
            user.getUsername(),
            user.getPassword(),
            Collections.emptyList()
        );
    }
}
创建一个SmsCode实体类,用于存储验证码信息:
@Data
public class SmsCode {
    private String code;
    private LocalDateTime expireTime;
    public SmsCode(String code, int expireIn) {
        this.code = code;
        this.expireTime = LocalDateTime.now().plusSeconds(expireIn);
    }
    public boolean isExpired() {
        return LocalDateTime.now().isAfter(expireTime);
    }
}
创建一个SmsCodeGenerator类,用于生成验证码:
@Component
public class SmsCodeGenerator {
    private static final String NUMBERS = "0123456789";
    public String generate(int length) {
        Random random = new Random();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            sb.append(NUMBERS.charAt(random.nextInt(NUMBERS.length())));
        }
        return sb.toString();
    }
}
创建一个SmsCodeService类,用于存储和验证验证码:
@Service
public class SmsCodeService {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private SmsCodeGenerator smsCodeGenerator;
    public void save(String phone, SmsCode smsCode) {
        redisTemplate.opsForValue().set(phone, smsCode.getCode(), 300, TimeUnit.SECONDS);
    }
    public SmsCode get(String phone) {
        String code = redisTemplate.opsForValue().get(phone);
        if (code == null) {
            return null;
        }
        return new SmsCode(code, 300);
    }
    public void remove(String phone) {
        redisTemplate.delete(phone);
    }
}
创建一个SmsCodeAuthenticationToken类,用于封装手机号和验证码:
public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {
    private static final long serialVersionUID = 1L;
    private final Object principal;
    public SmsCodeAuthenticationToken(String phone) {
        super(null);
        this.principal = phone;
        setAuthenticated(false);
    }
    public SmsCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        super.setAuthenticated(true);
    }
    @Override
    public Object getCredentials() {
        return null;
    }
    @Override
    public Object getPrincipal() {
        return this.principal;
    }
}
创建一个SmsCodeAuthenticationProvider类,用于验证手机号和验证码:
@Component
public class SmsCodeAuthenticationProvider implements AuthenticationProvider {
    @Autowired
    private SmsCodeService smsCodeService;
    @Autowired
    private UserDetailsService userDetailsService;
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String phone = (String) authentication.getPrincipal();
        SmsCode smsCode = smsCodeService.get(phone);
        if (smsCode == null || smsCode.isExpired()) {
            throw new BadCredentialsException("Invalid sms code");
        }
        smsCodeService.remove(phone);
        UserDetails userDetails = userDetailsService.loadUserByUsername(phone);
        return new SmsCodeAuthenticationToken(userDetails, userDetails.getAuthorities());
    }
    @Override
    public boolean supports(Class<?> authentication) {
        return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
    }
}
创建一个SmsCodeController类,用于处理验证码请求和手机登录请求:
@RestController
public class SmsCodeController {
    @Autowired
    private SmsCodeService smsCodeService;
    @Autowired
    private SmsCodeGenerator smsCodeGenerator;
    @PostMapping("/sms/code")
    public ResponseEntity<?> sendSmsCode(@RequestParam String phone) {
        String code = smsCodeGenerator.generate(6);
        smsCodeService.save(phone, new SmsCode(code, 300));
        // 这里可以调用短信服务发送验证码
        return ResponseEntity.ok().build();
    }
    @PostMapping("/sms/login")
    public ResponseEntity<?> smsLogin(@RequestParam String phone, @RequestParam String code) {
        SmsCode smsCode = smsCodeService.get(phone);
        if (smsCode == null || !smsCode.getCode().equals(code)) {
            throw new BadCredentialsException("Invalid sms code");
        }
        smsCodeService.remove(phone);
        // 这里可以返回JWT Token或其他认证信息
        return ResponseEntity.ok().build();
    }
}
启动应用后,你可以通过以下步骤测试:
/sms/code接口,发送验证码到手机。/sms/login接口,使用手机号和验证码登录。通过本文的介绍,你已经学会了如何使用Spring Boot整合Spring Security,实现账号密码和手机验证码两种登录方式。Spring Security提供了强大的扩展能力,你可以根据实际需求进一步定制和扩展认证流程。希望本文对你有所帮助,祝你在Spring Security的学习和使用中取得更多进展!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。