Spring Security Oauth2.0 实现短信验证码登录


声明:本文转载自https://my.oschina.net/giegie/blog/1605932,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

spring security oauth2 登录过程详解

oauth2登录过程详解.png

定义手机号登录令牌

/**  * @author lengleng  * @date 2018/1/9  * 手机号登录令牌  */ public class MobileAuthenticationToken extends AbstractAuthenticationToken {      private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;      private final Object principal;      public MobileAuthenticationToken(String mobile) {         super(null);         this.principal = mobile;         setAuthenticated(false);     }      public MobileAuthenticationToken(Object principal,                                      Collection<? extends GrantedAuthority> authorities) {         super(authorities);         this.principal = principal;         super.setAuthenticated(true);     }      public Object getPrincipal() {         return this.principal;     }      @Override     public Object getCredentials() {         return null;     }      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();     } } 

手机号登录校验逻辑

/**  * @author lengleng  * @date 2018/1/9  * 手机号登录校验逻辑  */ public class MobileAuthenticationProvider implements AuthenticationProvider {     private UserService userService;      @Override     public Authentication authenticate(Authentication authentication) throws AuthenticationException {         MobileAuthenticationToken mobileAuthenticationToken = (MobileAuthenticationToken) authentication;         UserVo userVo = userService.findUserByMobile((String) mobileAuthenticationToken.getPrincipal());          UserDetailsImpl userDetails = buildUserDeatils(userVo);         if (userDetails == null) {             throw new InternalAuthenticationServiceException("手机号不存在:" + mobileAuthenticationToken.getPrincipal());         }          MobileAuthenticationToken authenticationToken = new MobileAuthenticationToken(userDetails, userDetails.getAuthorities());         authenticationToken.setDetails(mobileAuthenticationToken.getDetails());         return authenticationToken;     }      private UserDetailsImpl buildUserDeatils(UserVo userVo) {         return new UserDetailsImpl(userVo);     }      @Override     public boolean supports(Class<?> authentication) {         return MobileAuthenticationToken.class.isAssignableFrom(authentication);     }      public UserService getUserService() {         return userService;     }      public void setUserService(UserService userService) {         this.userService = userService;     } } 

登录过程filter处理

**  * @author lengleng  * @date 2018/1/9  * 手机号登录验证filter  */ public class MobileAuthenticationFilter extends AbstractAuthenticationProcessingFilter {     public static final String SPRING_SECURITY_FORM_MOBILE_KEY = "mobile";      private String mobileParameter = SPRING_SECURITY_FORM_MOBILE_KEY;     private boolean postOnly = true;      public MobileAuthenticationFilter() {         super(new AntPathRequestMatcher(SecurityConstants.MOBILE_TOKEN_URL, "POST"));     }      public Authentication attemptAuthentication(HttpServletRequest request,                                                 HttpServletResponse response) throws AuthenticationException {         if (postOnly && !request.getMethod().equals(HttpMethod.POST.name())) {             throw new AuthenticationServiceException(                     "Authentication method not supported: " + request.getMethod());         }          String mobile = obtainMobile(request);          if (mobile == null) {             mobile = "";         }          mobile = mobile.trim();          MobileAuthenticationToken mobileAuthenticationToken = new MobileAuthenticationToken(mobile);          setDetails(request, mobileAuthenticationToken);          return this.getAuthenticationManager().authenticate(mobileAuthenticationToken);     }      protected String obtainMobile(HttpServletRequest request) {         return request.getParameter(mobileParameter);     }      protected void setDetails(HttpServletRequest request,                               MobileAuthenticationToken authRequest) {         authRequest.setDetails(authenticationDetailsSource.buildDetails(request));     }      public void setPostOnly(boolean postOnly) {         this.postOnly = postOnly;     }      public String getMobileParameter() {         return mobileParameter;     }      public void setMobileParameter(String mobileParameter) {         this.mobileParameter = mobileParameter;     }      public boolean isPostOnly() {         return postOnly;     } } 

生产token 位置

/**  * @author lengleng  * @date 2018/1/8  * 手机号登录成功,返回oauth token  */ @Component public class MobileLoginSuccessHandler implements org.springframework.security.web.authentication.AuthenticationSuccessHandler {     private Logger logger = LoggerFactory.getLogger(getClass());     @Autowired     private ObjectMapper objectMapper;     @Autowired     private ClientDetailsService clientDetailsService;     @Autowired     private AuthorizationServerTokenServices authorizationServerTokenServices;      @Override     public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {         String header = request.getHeader("Authorization");          if (header == null || !header.startsWith("Basic ")) {             throw new UnapprovedClientAuthenticationException("请求头中client信息为空");         }          try {             String[] tokens = extractAndDecodeHeader(header);             assert tokens.length == 2;             String clientId = tokens[0];             String clientSecret = tokens[1];              JSONObject params = new JSONObject();             params.put("clientId", clientId);             params.put("clientSecret", clientSecret);             params.put("authentication", authentication);              ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);             TokenRequest tokenRequest = new TokenRequest(MapUtil.newHashMap(), clientId, clientDetails.getScope(), "mobile");             OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);              OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);             OAuth2AccessToken oAuth2AccessToken = authorizationServerTokenServices.createAccessToken(oAuth2Authentication);             logger.info("获取token 成功:{}", oAuth2AccessToken.getValue());              response.setCharacterEncoding(CommonConstant.UTF8);             response.setContentType(CommonConstant.CONTENT_TYPE);             PrintWriter printWriter = response.getWriter();             printWriter.append(objectMapper.writeValueAsString(oAuth2AccessToken));         } catch (IOException e) {             throw new BadCredentialsException(                     "Failed to decode basic authentication token");         }     }      /**      * 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)             throws IOException {          byte[] base64Token = header.substring(6).getBytes("UTF-8");         byte[] decoded;         try {             decoded = Base64.decode(base64Token);         } catch (IllegalArgumentException e) {             throw new BadCredentialsException(                     "Failed to decode basic authentication token");         }          String token = new String(decoded, CommonConstant.UTF8);          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)};     } } 

配置以上自定义

//**  * @author lengleng  * @date 2018/1/9  * 手机号登录配置入口  */ @Component public class MobileSecurityConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {     @Autowired     private MobileLoginSuccessHandler mobileLoginSuccessHandler;     @Autowired     private UserService userService;      @Override     public void configure(HttpSecurity http) throws Exception {         MobileAuthenticationFilter mobileAuthenticationFilter = new MobileAuthenticationFilter();         mobileAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));         mobileAuthenticationFilter.setAuthenticationSuccessHandler(mobileLoginSuccessHandler);          MobileAuthenticationProvider mobileAuthenticationProvider = new MobileAuthenticationProvider();         mobileAuthenticationProvider.setUserService(userService);         http.authenticationProvider(mobileAuthenticationProvider)                 .addFilterAfter(mobileAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);     } } 

在spring security 配置 上边定一个的那个聚合配置

/**  * @author lengleng  * @date 2018年01月09日14:01:25  * 认证服务器开放接口配置  */ @Configuration @EnableResourceServer public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {     @Autowired     private FilterUrlsPropertiesConifg filterUrlsPropertiesConifg;     @Autowired     private MobileSecurityConfigurer mobileSecurityConfigurer;      @Override     public void configure(HttpSecurity http) throws Exception {         registry                 .antMatchers("/mobile/token").permissionAll()                 .anyRequest().authenticated()                 .and()                 .csrf().disable();         http.apply(mobileSecurityConfigurer);     } } 

使用

curl -H "Authorization:Basic cGlnOnBpZw==" -d "grant_type=mobile&scope=server&mobile=17034642119&code=" http://localhost:9999/auth/mobile/token 

源码

  1. 请参考 https://gitee.com/log4j/
  2. 基于Spring Cloud、Spring Security Oauth2.0开发企业级认证与授权,提供常见服务监控、链路追踪、日志分析、缓存管理、任务调度等实现
  3. 整个逻辑是参考spring security 自身的 usernamepassword 登录模式实现,可以参考其源码。
  4. 验证码的发放、校验逻辑比较简单,方法后通过全局fiter 判断请求中code 是否和 手机号匹配集合,重点逻辑是令牌的参数

本文发表于2018年01月12日 10:32
(c)注:本文转载自https://my.oschina.net/giegie/blog/1605932,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 2097 讨论 1 喜欢 1

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1