Spring Security(十三):认证(OAuth2)

您所在的位置:网站首页 java如何实现手机验证码登录微信 Spring Security(十三):认证(OAuth2)

Spring Security(十三):认证(OAuth2)

2024-07-16 22:47| 来源: 网络整理| 查看: 265

一:简介

通过短信验证码获取令牌的过程也是自定义认证的过程。 组定义认证流程如下:

自定义一个过滤器实现认证 SmsCodeAuthenticationFilter认证的时候需要将认证信息封装到一个令牌实体中 SmsCodeAuthenticationToken最终实现认证的是认证提供商 SmsCodeAuthenticationProvider将自定义的过滤器添加到UsernamePasswordAuthenticationFilter过滤器的前面 二:自定义认证流程 pom.xml 4.0.0 org.springframework.boot spring-boot-starter-parent 2.1.5.RELEASE com.example springboot-security-example 0.0.1-SNAPSHOT springboot-security-example Demo project for Spring Boot 1.8 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-security org.springframework.security.oauth.boot spring-security-oauth2-autoconfigure 2.1.2.RELEASE org.springframework.security.oauth spring-security-oauth2 2.3.4.RELEASE compile org.springframework.boot spring-boot-starter-data-redis redis.clients jedis 2.9.0 org.projectlombok lombok org.springframework.boot spring-boot-starter-test org.springframework.boot spring-boot-maven-plugin application.yml server: port: 8080 # Redis数据库索引(默认为0) spring: redis: database: 0 host: localhost port: 6379 password: logging: level: org.springframework: debug SmsCodeAuthenticationFilter public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter { public SmsCodeAuthenticationFilter(String loginProcessUrlMobile) { super(new AntPathRequestMatcher(loginProcessUrlMobile, "POST")); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (!request.getMethod().equals("POST")) { throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); } String mobile = obtainMobile(request); // 封装令牌 SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile); // Allow subclasses to set the "details" property setDetails(request, authRequest); // 开始认证 return this.getAuthenticationManager().authenticate(authRequest); } protected String obtainMobile(HttpServletRequest request) { String mobile = request.getParameter("mobile"); if (mobile == null) { mobile = ""; } return mobile.trim(); } protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) { authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); } } SmsCodeAuthenticationToken public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken { private static final long serialVersionUID = 1L; /** 身份 */ private final Object principal; public SmsCodeAuthenticationToken(Object mobile) { super(null); this.principal = mobile; setAuthenticated(false); } public SmsCodeAuthenticationToken(Object principal, Collection authorities) { super(authorities); this.principal = principal; // must use super, as we override super.setAuthenticated(true); } @Override public Object getPrincipal() { return this.principal; } @Override 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(); } @Override public Object getCredentials() { return null; } } SmsCodeAuthenticationProvider public class SmsCodeAuthenticationProvider implements AuthenticationProvider { private UserDetailsService userDetailsService; @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication; UserDetails user = userDetailsService.loadUserByUsername((String) authenticationToken.getPrincipal()); if (user == null) { throw new InternalAuthenticationServiceException("无法获取用户信息"); } // TODO 在这里校验验证码是否正确,验证码一般存放到redis中 SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(user, user.getAuthorities()); authenticationResult.setDetails(authenticationToken.getDetails()); return authenticationResult; } @Override public boolean supports(Class authentication) { return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication); } public UserDetailsService getUserDetailsService() { return userDetailsService; } public void setUserDetailsService(UserDetailsService userDetailsService) { this.userDetailsService = userDetailsService; } } SmsCodeAuthenticationSecurityConfig @Component public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter { @Autowired private AuthenticationSuccessHandler authenticationSuccessHandler; @Autowired private AuthenticationFailureHandler authenticationFailureHandler; @Autowired private UserDetailsService userDetailsService; @Override public void configure(HttpSecurity http) throws Exception { SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter("/authentication/mobile"); smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class)); smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler); smsCodeAuthenticationFilter.setAuthenticationFailureHandler(authenticationFailureHandler); SmsCodeAuthenticationProvider provider = new SmsCodeAuthenticationProvider(); provider.setUserDetailsService(userDetailsService); // 将SmsCodeAuthenticationFilter放到过滤器链的UsernamePasswordAuthenticationFilter的后面 http .authenticationProvider(provider) .addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); } } MyAuthenticationSuccessHandler /** * 登录成功时执行 * * @author Mengday Zhang * @version 1.0 * @since 2019-04-20 */ @Slf4j @Component public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { @Autowired private ObjectMapper objectMapper; @Autowired private ClientDetailsService clientDetailsService; @Autowired private AuthorizationServerTokenServices authorizationServerTokenServices; @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { log.info("login sucesssful {}", objectMapper.writeValueAsString(authentication)); String header = request.getHeader("Authorization"); if (header == null || !header.toLowerCase().startsWith("basic ")) { throw new UnapprovedClientAuthenticationException("请求头中没有clientId"); } String[] tokens = extractAndDecodeHeader(header, request); assert tokens.length == 2; String clientId = tokens[0]; String clientSecret = tokens[1]; ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId); if (clientDetails == null) { throw new UnapprovedClientAuthenticationException("clientId配置信息不存在,clientId=" + clientId); } else if (!new BCryptPasswordEncoder().matches(clientSecret, clientDetails.getClientSecret())) { throw new UnapprovedClientAuthenticationException("clientSecret不匹配,clientId=" + clientId); } // grantType 为自定义的"custom" TokenRequest tokenRequest = new TokenRequest(new HashMap(), clientId, clientDetails.getScope(), "custom"); OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails); OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication); OAuth2AccessToken accessToken = authorizationServerTokenServices.createAccessToken(oAuth2Authentication); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(objectMapper.writeValueAsString(accessToken)); } /** * Decodes the header into a username and password. * * @throws BadCredentialsException if the Basic header is not present or is not valid * Base64 */ private String[] extractAndDecodeHeader(String header, HttpServletRequest request) throws IOException { byte[] base64Token = header.substring(6).getBytes("UTF-8"); byte[] decoded; try { decoded = Base64.getDecoder().decode(base64Token); } catch (IllegalArgumentException e) { throw new BadCredentialsException( "Failed to decode basic authentication token"); } String token = new String(decoded, "UTF-8"); int delim = token.indexOf(":"); if (delim == -1) { throw new BadCredentialsException("Invalid basic authentication token"); } return new String[] { token.substring(0, delim), token.substring(delim + 1) }; } } MyAuthenticationFailureHandler @Slf4j @Component public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { log.info("认证失败"); response.setContentType("text/html;charset=utf-8"); response.getWriter().write(exception.getMessage()); } } SecurityConfiguration @Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler; @Autowired private MyAuthenticationFailureHandler myAuthenticationFailureHandler; @Autowired private MyUserDetailsService myUserDetailsService; @Autowired private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig; @Override protected void configure(HttpSecurity http) throws Exception { http .apply(smsCodeAuthenticationSecurityConfig) .and() .formLogin() .loginPage("/authentication/form") .successHandler(myAuthenticationSuccessHandler) .failureHandler(myAuthenticationFailureHandler); http.authorizeRequests() .antMatchers("/login", "/authentication/form", "/authentication/mobile", "/code/sms").permitAll() .anyRequest().authenticated() .and() .csrf().disable(); } @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder()); } @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } } AuthorizationServerConfiguration @Configuration @EnableAuthorizationServer public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() // 使用in-memory存储 .withClient("clientId") .secret(new BCryptPasswordEncoder().encode("clientSecret")) .authorizedGrantTypes("authorization_code") .scopes("all") .redirectUris("http://www.baidu.com"); } @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security .tokenKeyAccess("permitAll()") .checkTokenAccess("permitAll()") .allowFormAuthenticationForClients(); } } ResourceServerConfiguration @Configuration @EnableResourceServer public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { @Autowired private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler; @Autowired private MyAuthenticationFailureHandler myAuthenticationFailureHandler; @Autowired private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig; @Override public void configure(HttpSecurity http) throws Exception { http .apply(smsCodeAuthenticationSecurityConfig) .and() .formLogin() .loginPage("/authentication/form") .successHandler(myAuthenticationSuccessHandler) .failureHandler(myAuthenticationFailureHandler); http.authorizeRequests() .antMatchers("/login", "/authentication/form", "/authentication/mobile", "/code/sms").permitAll() .anyRequest().authenticated() .and() .csrf().disable(); } } SmsValidateCodeController @RestController public class SmsValidateCodeController { @Autowired private StringRedisTemplate stringRedisTemplate; @GetMapping(value = "/code/sms", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) public SmsCode createCode(@RequestHeader("deviceId") String deviceId, String mobile) { SmsCode smsCode = createSmsCode(); System.out.println("验证码发送成功:" + smsCode); String key = "code:sms:"+ deviceId; stringRedisTemplate.opsForValue().set(key, smsCode.getCode()); return smsCode; } private SmsCode createSmsCode() { String code = (int) ((Math.random() * 9 + 1) * 100000) + ""; return new SmsCode(code, 30000); } } 三:获取token 1. 发送短信验证码

在这里插入图片描述

2. 获取token 1. 通过rest client 获取token

在这里插入图片描述

在这里插入图片描述

2. 通过curl命令行获取token

通过上面的Copy as cURL按钮获取curl命令行请求,然后执行命令行获取token(在命令行上都能获取token,那么app上更能获取到token) 在这里插入图片描述



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3