🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
1. `pom`文件 ```xml <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.6.RELEASE</version> <relativePath/> </parent> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> ``` 2. `application.yml`文件 ```yaml server: port: 80 ``` 3. 入口类 ```java @SpringBootApplication public class SpringSecurityApplication { public static void main(String[] args) { SpringApplication.run(SpringSecurityApplication.class, args); } } ``` 4. 登录页`resources/static/login.html` ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Login</title> </head> <body> <form action="/login/mobile" method="post"> <div class="form"> <h3>短信验证码登录</h3> <input type="text" placeholder="手机号" name="mobile" value="13688125555" required="required"/> <input type="text" name="smsCode" placeholder="短信验证码"/> <a href="/code/sms?mobile=13688125555">发送验证码</a> <button type="submit">登录</button> </div> </form> </body> </html> ``` 5. 控制器(资源) ```java @RestController public class HomeController { @GetMapping("/") public String home() { return "hello spring security"; } } ``` 6. 控制器(发送验证码) ```java @RestController public class ValidateController { public final static String SESSION_KEY_SMS_CODE = "SESSION_KEY_SMS_CODE"; @GetMapping("/code/sms") public void createSmsCode(HttpServletRequest request, HttpServletResponse response, String mobile) throws IOException { SmsCode smsCode = createSMSCode(); request.getSession().setAttribute(SESSION_KEY_SMS_CODE + mobile, smsCode); sendSms(smsCode.getCode()); } private SmsCode createSMSCode() { String code = "666666"; return new SmsCode(code, 60); } private void sendSms(String code) { System.out.println("您的登录验证码为:" + code + ",有效时间为60秒"); } } ``` 7. 短信类 ```java 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 SmsCode(String code, LocalDateTime expireTime) { this.code = code; this.expireTime = expireTime; } boolean isExpire() { return LocalDateTime.now().isAfter(expireTime); } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public LocalDateTime getExpireTime() { return expireTime; } public void setExpireTime(LocalDateTime expireTime) { this.expireTime = expireTime; } } ``` 8. 短信认证类 ```java public class SmsAuthenticationToken extends AbstractAuthenticationToken { private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; private final Object principal; public SmsAuthenticationToken(Object principal) { super(null); this.principal = principal; setAuthenticated(false); } public SmsAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) { super(authorities); this.principal = principal; super.setAuthenticated(true); } @Override public Object getCredentials() { return null; } public Object getPrincipal() { return this.principal; } public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { if (isAuthenticated) { throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead"); } super.setAuthenticated(false); } @Override public void eraseCredentials() { super.eraseCredentials(); } } ``` 9. UserDetailsService ```java @Service public class MyUserDetailsService implements UserDetailsService { @Autowired private PasswordEncoder passwordEncoder; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return new User("user", passwordEncoder.encode("user"), Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"))); } } ``` 10. 校验短信验证码`SmsCodeFilter` ```java @Component public class SmsCodeFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { // TODO 校验code 根据请求url,判断是否是短信登录请求 System.out.println("校验code..."); filterChain.doFilter(httpServletRequest, httpServletResponse); } } ``` 11. 短信认证过滤器 ```java public class SmsAuthenticationFilter extends AbstractAuthenticationProcessingFilter { public static final String MOBILE_KEY = "mobile"; private String mobileParameter = MOBILE_KEY; private boolean postOnly = true; public SmsAuthenticationFilter() { super(new AntPathRequestMatcher("/login/mobile", "POST")); } 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 mobile = obtainMobile(request); if (mobile == null) { mobile = ""; } mobile = mobile.trim(); SmsAuthenticationToken authRequest = new SmsAuthenticationToken(mobile); setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } protected String obtainMobile(HttpServletRequest request) { return request.getParameter(mobileParameter); } protected void setDetails(HttpServletRequest request, SmsAuthenticationToken authRequest) { authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); } public void setMobileParameter(String mobileParameter) { Assert.hasText(mobileParameter, "mobile parameter must not be empty or null"); this.mobileParameter = mobileParameter; } public void setPostOnly(boolean postOnly) { this.postOnly = postOnly; } public final String getMobileParameter() { return mobileParameter; } } ``` 12. 短信认证提供者 ```java public class SmsAuthenticationProvider implements AuthenticationProvider { private UserDetailsService userDetailsService; @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { SmsAuthenticationToken authenticationToken = (SmsAuthenticationToken) authentication; UserDetails userDetails = userDetailsService.loadUserByUsername((String) authenticationToken.getPrincipal()); if (userDetails == null) throw new InternalAuthenticationServiceException("未找到与该手机号对应的用户"); SmsAuthenticationToken authenticationResult = new SmsAuthenticationToken(userDetails, userDetails.getAuthorities()); authenticationResult.setDetails(authenticationToken.getDetails()); return authenticationResult; } @Override public boolean supports(Class<?> aClass) { return SmsAuthenticationToken.class.isAssignableFrom(aClass); } public UserDetailsService getUserDetailsService() { return userDetailsService; } public void setUserDetailsService(UserDetailsService userDetailsService) { this.userDetailsService = userDetailsService; } } ``` 13. 短信认证配置 ```java @Component public class SmsAuthenticationConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> { @Autowired private UserDetailsService userDetailsService; @Override public void configure(HttpSecurity http) throws Exception { SmsAuthenticationFilter smsAuthenticationFilter = new SmsAuthenticationFilter(); smsAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class)); SmsAuthenticationProvider smsAuthenticationProvider = new SmsAuthenticationProvider(); smsAuthenticationProvider.setUserDetailsService(userDetailsService); http.authenticationProvider(smsAuthenticationProvider) .addFilterAfter(smsAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); } } ``` 14. 总配置 ```java @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private SmsCodeFilter smsCodeFilter; @Autowired private SmsAuthenticationConfig smsAuthenticationConfig; @Override protected void configure(HttpSecurity http) throws Exception { http.addFilterBefore(smsCodeFilter, UsernamePasswordAuthenticationFilter.class); http.apply(smsAuthenticationConfig); http.formLogin() .loginPage("/login.html") .loginProcessingUrl("/login") .and() .authorizeRequests() .antMatchers("/login.html", "/code/sms").permitAll() .anyRequest() .authenticated() .and() .csrf().disable(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } } ```