Spring Security OAuth2 Login process details

Define the mobile number login token

/** * @author lengleng * @date 2018/1/9 * Phone number login token */
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(); }}Copy the code

Mobile phone number login verification logic

/** * @author lengleng * @date 2018/1/9 * Phone number login verification logic */
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("Mobile phone number does not exist :" + mobileAuthenticationToken.getPrincipal());
        }

        MobileAuthenticationToken authenticationToken = new MobileAuthenticationToken(userDetails, userDetails.getAuthorities());
        authenticationToken.setDetails(mobileAuthenticationToken.getDetails());
        return authenticationToken;
    }

    private UserDetailsImpl buildUserDeatils(UserVo userVo) {
        return newUserDetailsImpl(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; }}Copy the code

Login process Filter

* * * @ author lengleng * @ date 2018/1/9 logon authentications filter * * phone number/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; }}Copy the code

Production token location

/** * @author lengleng * @date 2018/1/8 * Mobile number Oauth Token */ is returned
@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 information in request header is empty");
        }

        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("Succeeded in obtaining 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)}; }}Copy the code

Configure the above customization

//** * @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); }}Copy the code

The aggregation configuration specified above the Spring Security configuration

/** * @author Lengleng * @date 2018年01月09日14:01:25 * Authentication server open interface configuration */
@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); }}Copy the code

use

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

The source code

  1. Please refer to the gitee.com/log4j/
  2. Develop enterprise-level authentication and authorization based on Spring Cloud and Spring Security Oauth2.0, and provide common service monitoring, link tracking, log analysis, cache management, task scheduling, etc
  3. The whole logic is to refer to spring Security’s own userNamepassword login mode implementation, you can refer to its source code.
  4. The issuing and verification logic of the verification code is relatively simple. After the method, the global FITer is used to determine whether the code in the request matches the set of the mobile phone number. The key logic is the parameter of the token