A set of solutions for Spring Security OAuth2 authorization mode extension and application practice

1, Foreword

[APP mobile terminal] Spring Security OAuth2 SMS verification code mode

[wechat applet] Spring Security OAuth2 wechat authorization mode

[management system] Spring Security OAuth2 password mode

[management system] Spring Security OAuth2 verification code mode

The four authorization modes implemented by Spring Security OAuth2 by default often fail to meet expectations in actual application scenarios, such as the following requirements:

  1. The authorization object is divided into multiple user systems, such as system users and member users;
  2. Add a verification code verification on the basis of password authorization mode;
  3. Realize mobile phone and SMS verification code login based on Spring Security OAuth2;
  4. Authorized login of wechat applet based on Spring Security OAuth2.

I believe you will encounter but not limited to the above scenarios. There are many articles on the extension of Spring Security OAuth2 authorization mode on the Internet, but there are some common problems of incompleteness and complex implementation, which once made you feel that Spring Security OAuth2 is difficult. Spring also provides many extension points on the basis of realizing core functions, as well as Spring Security OAuth2, I believe this article will help eliminate its difficult misunderstanding.

This chapter will expand the Spring Security OAuth2 authorization mode based on actual combat and supplemented by principles, in the principle of comprehensive and minimum changes. The contents of this chapter are as follows:

  1. Spring Cloud Gateway microservice gateway WebFlux integrates Google verification code   Kaptchaï¼›
  2. SpringBoot integrates Alibaba cloud SMS service;
  3. Analysis of the underlying source code of Spring Security OAuth2 authentication and authorization mode;
  4. Spring Security OAuth2 extended authentication code authorization mode;
  5. Spring Security OAuth2 extends the authorization mode of SMS verification code;
  6. Spring Security OAuth2 extends wechat authorization mode;
  7. Spring Security OAuth2 multi-user system refresh mode;
  8. Vue element admin background management front-end login access verification code authorization mode;
  9. Uni app wechat applet login and access wechat authorization mode;
  10. Uni app H5, mobile terminal mobile phone verification code login access SMS verification code authorization mode.

Let's make a very important statement first. This article covers all code addresses:

entry name

Code cloud (Gitee)

GitHub

Microservice background

youlai-mall

youlai-mall

Management front end

mall-admin-web

mall-admin-web

Wechat Applet / H5/Android/IOS

mall-app

mall-app

Because it involves a lot of content, it is impossible to completely post all the code in the article, but rest assured that the source code is all online, and the same documents are available

2, Verification code authorization mode

1. Principle

The verification code authorization mode is to add a verification code verification based on the password mode. If you have the mentality that no matter what Kung Fu, what can win you is good Kung Fu, you can use the filter, but if you can't think about it, try to expand it.

Because it is an extension based on password authorization mode, let's first understand the process of password authorization mode. Because the implementation principles of other authorization modes and password modes are the same, after understanding the password authorization mode, other authorization modes, including how to expand, are familiar.

Password mode process:   According to the request parameter grant_ The value password of type matches the authorizer
ResourceOwnerPasswordTokenGraner: the authorizer delegates to the authentication Provider manager ProviderManager and matches the Provider DaoAuthenticationProvider according to the token type. The Provider obtains the user authentication information and the user information requested by the client from the database to interpret the authentication password. After passing the authentication, it returns the token to the client.

The following sequence diagram of password authorization mode shows the key classes and methods. You should know the process after walking through the breakpoint several times.

The sequence diagram of the authentication code authorization mode is as follows. Carefully compare the differences between the authentication code authorization mode and the password authorization mode.

The comparison shows that the difference between the two is basically the difference between the authorizer and the Granter. The subsequent providers obtain user authentication information and password judgment are completely consistent. Specifically, the newly added authenticator in CaptchaTokenGranter and the authorizer in password mode
The difference of ResourceOwnerPasswordTokenGraner is that the getoauthauthentication () method of the former obtains the authentication information and adds the logic of verifying the verification code. The specific code implementation is explained in the actual battle.

2. Actual combat

The verification code authorization mode involves three parts: Spring Security OAuth2 extended verification code authorization mode, verification code generated in the background and verification code added by front-end login. It involves front-end and back-end things. You can select your own concerns according to your needs.

2.1 extension of authentication code authorization mode

It is known from the principle that only the ability of Granter to add verification code needs to be rewritten, so the authorizer who copies the password mode
ResourceOwnerPasswordTokenGranter is renamed CaptchaTokenGranter and becomes the authorizer of the verification code mode with minor changes.

CaptchaTokenGranter

Copy code 12345678910112131415161718192021222324252627282930333436373839404142434445464748495051525354555657585906162636465666768697071727374757677 JAVA/**
 * Verification code authorization mode authorizer
 *
 * @author <a href="mailto:xianrui0365@163.com">xianrui</a>
 * @date 2021/9/25
 */
public class CaptchaTokenGranter extends AbstractTokenGranter {

    /**
     * Claim that the authorizer CaptchaTokenGranter supports the authorization mode captcha
     * Pass the value grant according to the interface_ The value of type = captcha matches this authorizer
     * See the following two methods for matching logic
     *
     * @see org.springframework.security.oauth2.provider.CompositeTokenGranter#grant(String, TokenRequest)
     * @see org.springframework.security.oauth2.provider.token.AbstractTokenGranter#grant(String, TokenRequest)
     */
    private static final String GRANT_TYPE = "captcha";
    private final AuthenticationManager authenticationManager;
    private StringRedisTemplate redisTemplate;

    public CaptchaTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService,
                               OAuth2RequestFactory requestFactory, AuthenticationManager authenticationManager,
                               StringRedisTemplate redisTemplate
    ) {
        super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
        this.authenticationManager = authenticationManager;
        this.redisTemplate = redisTemplate;
    }

    @Override
    protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {

        Map<String, String> parameters = new LinkedHashMap(tokenRequest.getRequestParameters());

        // Verification code verification logic
        String validateCode = parameters.get("validateCode");
        String uuid = parameters.get("uuid");

        Assert.isTrue(StrUtil.isNotBlank(validateCode), "Verification code cannot be empty");
        String validateCodeKey = AuthConstants.VALIDATE_CODE_PREFIX + uuid;
        
        // Extract the correct verification code from the cache and compare it with the verification code entered by the user
        String correctValidateCode = redisTemplate.opsForValue().get(validateCodeKey);
        if (!validateCode.equals(correctValidateCode)) {
            throw new BizException("Incorrect verification code");
        } else {
            redisTemplate.delete(validateCodeKey);
        }

        String username = parameters.get("username");
        String password = parameters.get("password");

        // Remove subsequent useless parameters
        parameters.remove("password");
        parameters.remove("validateCode");
        parameters.remove("uuid");

        // Same logic as password mode
        Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);
        ((AbstractAuthenticationToken) userAuth).setDetails(parameters);

        try {
            userAuth = this.authenticationManager.authenticate(userAuth);
        } catch (AccountStatusException var8) {
            throw new InvalidGrantException(var8.getMessage());
        } catch (BadCredentialsException var9) {
            throw new InvalidGrantException(var9.getMessage());
        }

        if (userAuth != null && userAuth.isAuthenticated()) {
            OAuth2Request storedOAuth2Request = this.getRequestFactory().createOAuth2Request(client, tokenRequest);
            return new OAuth2Authentication(storedOAuth2Request, userAuth);
        } else {
            throw new InvalidGrantException("Could not authenticate user: " + username);
        }
    }
}

Two changes have been made to the authorizer of password mode, which are summarized as follows:

  1. Modify grant_ Value of type   password   by   captcha;
  2. The getOAuth2Authentication() method adds verification code verification logic.

AuthorizationServerConfig

Rewrite TokenGranter in the AuthorizationServerConfig configuration class to support the newly added verification code mode authorizer CaptchaTokenGranter

Now, the Spring Security OAuth2 extended authentication code authorization is complete!!!

How's it going, Jane? It's not easy? I believe you may have doubts. Let's take a test first.

The client ID of the management front-end is mall admin web. Before testing, the client is given to support the verification code mode.

Enter the wrong verification code and the correct verification code in the login interface to see whether the effect can achieve the expected effect, and how to generate the verification code and how to transfer the value from the front end will be described later.

2.2 Spring WebFlux integration verification code Kaptcha

The function of verification code generation is to generate a random code, cache it in redis, and return the key ID (generally uuid) of redis and the picture of the random code to the front end. Because there is no business logic, it is directly placed in the gateway here. In addition to taking advantage of the performance advantages of WebFlux, it can also reduce one forwarding. The code structure diagram of Youlai gateway verification code is as follows:

CaptchaHandler

Copy code 123456789101121314151617181920212223242526272829303132 JAVA@Component
@RequiredArgsConstructor
public class CaptchaHandler implements HandlerFunction<ServerResponse> {

    private final Producer producer;
    private final StringRedisTemplate redisTemplate;

    @Override
    public Mono<ServerResponse> handle(ServerRequest serverRequest) {
        // Generate verification code
        String capText = producer.createText();
        String capStr = capText.substring(0, capText.lastIndexOf("@"));
        String code = capText.substring(capText.lastIndexOf("@") + 1);
        BufferedImage image = producer.createImage(capStr);
        // Cache verification code to Redis
        String uuid = IdUtil.simpleUUID();
        redisTemplate.opsForValue().set(AuthConstants.VALIDATE_CODE_PREFIX + uuid, code, 60, TimeUnit.SECONDS);
        // Conversion stream information write out
        FastByteArrayOutputStream os = new FastByteArrayOutputStream();
        try {
            ImageIO.write(image, "jpg", os);
        } catch (IOException e) {
            return Mono.error(e);
        }

        java.util.Map resultMap = new HashMap<String, String>();
        resultMap.put("uuid", uuid);
        resultMap.put("img", Base64.encode(os.toByteArray()));

        return ServerResponse.status(HttpStatus.OK).body(BodyInserters.fromValue(Result.success(resultMap)));
    }
}

CaptchaConfig

The property kaptcha.textproducer.impl needs to specify the classpath of your own project text generator KaptchaTextCreator

Copy code 12 JAVA// Verification code text generator 
properties.setProperty("kaptcha.textproducer.impl", "com.youlai.gateway.kaptcha.KaptchaTextCreator");

CaptchaRouter

Copy code 12345678910 JAVA@Configuration
public class CaptchaRouter {

    @Bean
    public RouterFunction<ServerResponse> routeFunction(CaptchaHandler captchaHandler) {
        return RouterFunctions
                .route(RequestPredicates.GET("/captcha")
                        .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), captchaHandler::handle);
    }
}

Verification code test

Modify the Nacos gateway configuration file youlai-gateway.yaml whitelist add request path / captcha

visit
http://localhost:9999/captcha As follows:

2.3 front end login access verification code mode

Login page

Add a verification code to the login form. The complete code address is: Mall admin web

src/views/login/index.vue

Copy code 12345678910112131415 HTML <el-form-item prop="validateCode">
    <span class="svg-container">
       <svg-icon icon-class="validCode"/>
     </span>
   <el-input
     v-model="loginForm.validateCode"
     auto-complete="off"
     placeholder="Please enter the verification code"
     style="width: 65%"
     @keyup.enter.native="handleLogin"
   />
   <div class="validate-code">
     <img :src="captchaUrl" @click="getValidateCode" height="38px"/>
   </div>
 </el-form-item>

The returned image is a Base64 encrypted string, so the prefix data: image / GIF is added; base64,

Copy code 12345678 JAVASCRIPT// Get verification code
getValidateCode() {
  getCaptcha().then(response => {
	const {img, uuid} = response.data
	this.captchaUrl = "data:image/gif;base64," + img
	this.loginForm.uuid = uuid;
  })
}

Interface request

src/store/modules/user.js set request parameters

Copy code 123456789101121314151617181920 JAVASCRIPTlogin({commit}, userInfo) {
  const {username, password, validateCode, uuid} = userInfo
  return new Promise((resolve, reject) => {
    login({  
      username: username,
      password: password,
      grant_type: 'captcha', // The authorization mode is specified as captcha verification code mode, which was originally password password mode
      uuid: uuid, // Obtain the identification of the correct verification code from Redis
      validateCode: validateCode // Verification Code
    }).then(response => {
      const {access_token, refresh_token, token_type} = response.data
      const token = token_type + " " + access_token
      commit('SET_TOKEN', token)
      setToken(token)
      setRefreshToken(refresh_token)
      resolve()
    }).catch(error => {
      reject(error)
    })
  })

src/api/user.js set request header

Copy code 12345678910 JAVASCRIPTexport function login(params) {
  return request({
    url: '/youlai-auth/oauth/token',
    method: 'post',
    params: params,
    headers: {
      'Authorization': 'Basic bWFsbC1hZG1pbi13ZWI6MTIzNDU2' // OAuth2 client information Base64 encryption, plaintext: Mall admin Web: 123456
    }
  })
}

3, Authorization mode of SMS verification code

1. Principle

The sequence diagram of SMS verification code mode is as follows. The changed roles are still marked with green background. You can see that the extension is the entry point for the authorizer Granter and the authentication Provider provider.

Authorization process of SMS verification code:   The process is basically consistent with the password mode, according to grant_ The type matches the authorizer, smscodeokengranter, and delegates it to the ProviderManager for authentication according to
Matching authentication provider for SmsCodeAuthenticationToken   SmsCodeAuthenticationProvider performs SMS verification code verification.

2. Actual combat

2.1 authorization mode extension of SMS verification code

SmsCodeTokenGranter

Copy code 123456789101121314151617181920212223242526272829303313233435363738394041424344454647484950515253545556 JAVA/**
 * Mobile phone verification code authorizer
 *
 * @author <a href="mailto:xianrui0365@163.com">xianrui</a>
 * @date 2021/9/25
 */
public class SmsCodeTokenGranter extends AbstractTokenGranter {

    /**
     * SMS claims that the authorizer CaptchaTokenGranter supports the authorization mode_ code
     * Pass the value grant according to the interface_ type = sms_ The value of code matches this authorizer
     * See the following two methods for matching logic
     *
     * @see org.springframework.security.oauth2.provider.CompositeTokenGranter#grant(String, TokenRequest)
     * @see org.springframework.security.oauth2.provider.token.AbstractTokenGranter#grant(String, TokenRequest)
     */
    private static final String GRANT_TYPE = "sms_code";
    private final AuthenticationManager authenticationManager;

    public SmsCodeTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService,
                               OAuth2RequestFactory requestFactory, AuthenticationManager authenticationManager
    ) {
        super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
        this.authenticationManager = authenticationManager;
    }

    @Override
    protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {

        Map<String, String> parameters = new LinkedHashMap(tokenRequest.getRequestParameters());

        String mobile = parameters.get("mobile"); // cell-phone number
        String code = parameters.get("code"); // SMS verification code

        parameters.remove("code");

        Authentication userAuth = new SmsCodeAuthenticationToken(mobile, code);
        ((AbstractAuthenticationToken) userAuth).setDetails(parameters);

        try {
            userAuth = this.authenticationManager.authenticate(userAuth);
        } catch (AccountStatusException var8) {
            throw new InvalidGrantException(var8.getMessage());
        } catch (BadCredentialsException var9) {
            throw new InvalidGrantException(var9.getMessage());
        }

        if (userAuth != null && userAuth.isAuthenticated()) {
            OAuth2Request storedOAuth2Request = this.getRequestFactory().createOAuth2Request(client, tokenRequest);
            return new OAuth2Authentication(storedOAuth2Request, userAuth);
        } else {
            throw new InvalidGrantException("Could not authenticate user: " + mobile);
        }
    }
}

SmsCodeAuthenticationProvider

Copy code 12345678910112131415161718192021222324252627282930331323343536373839 JAVA/**
 * SMS verification code authentication authorization provider
 *
 * @author <a href="mailto:xianrui0365@163.com">xianrui</a>
 * @date 2021/9/25
 */
@Data
public class SmsCodeAuthenticationProvider implements AuthenticationProvider {

    private UserDetailsService userDetailsService;
    private MemberFeignClient memberFeignClient;
    private StringRedisTemplate redisTemplate;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication;
        String mobile = (String) authenticationToken.getPrincipal();
        String code = (String) authenticationToken.getCredentials();

        String codeKey = AuthConstants.SMS_CODE_PREFIX + mobile;
        String correctCode = redisTemplate.opsForValue().get(codeKey);
        // Verification code comparison
        if (StrUtil.isBlank(correctCode) || !code.equals(correctCode)) {
            throw new BizException("Incorrect verification code");
        } else {
            redisTemplate.delete(codeKey);
        }
        UserDetails userDetails = ((MemberUserDetailsServiceImpl) userDetailsService).loadUserByMobile(mobile);
        WechatAuthenticationToken result = new WechatAuthenticationToken(userDetails, new HashSet<>());
        result.setDetails(authentication.getDetails());
        return result;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

AuthorizationServerConfig

In the authentication center configuration, add the SmsCodeTokenGranter to the set of authorization types of the authenticator.

2.2 Alibaba cloud free SMS application

visit
https://free.aliyun.com/product/cloudcommunication-free-trial?spm=5176.10695662.1128094.7.2a6b4bee30xtJx Apply for Alibaba cloud free SMS trial

Add signature and wait for approval

After the signature is approved, you can create an AccessKey access key

Add template, domestic message → template management → add template

After the signature is approved, the AccessKey and template CODE are obtained, and then the project integration can be carried out.

2.3 SpringBoot integrates alicloud SMS

There are many online tutorials on integrating SMS with SpringBoot. There is no icing on the cake here. Next, let's briefly talk about integrating Alibaba cloud SMS with Youlai mall. Complete source code

By convention, the SMS is encapsulated into a public module for reference to other application modules that need SMS.

Youlai auth introduces common SMS dependency

Copy code 123456 XML<dependencies> 
    <dependency>
        <groupId>com.youlai</groupId>
        <artifactId>common-sms</artifactId>
    </dependency>
</dependencies>

The properties required by aliyunsmproperties need to be configured in the configuration center file youlai-auth.yaml of Nacos

Copy code 123456789 YAML# Alibaba cloud SMS configuration
aliyun:
  sms:
    accessKeyId: LTAI5tSxxxxxxNcD6diBJLyR
    accessKeySecret: SoOWRqpjtSxxxxxxM8QZ2PZiMTJOVC
    domain: dysmsapi.aliyuncs.com 
    regionId: cn-shanghai
    templateCode: SMS_225xxx770
    signName: Have come to technology

Send SMS verification code interface

Copy code 1234567891011213141516 JAVA@Api(tags = "SMS verification code")
@RestController
@RequestMapping("/sms-code")
@RequiredArgsConstructor
public class SmsCodeController {

    private final AliyunSmsService aliyunSmsService;

    @ApiOperation(value = "Send SMS verification code")
    @ApiImplicitParam(name = "phoneNumber", example = "17621590365", value = "cell-phone number", required = true)
    @PostMapping
    public Result sendSmsCode(String phoneNumber)  {
        boolean result = aliyunSmsService.sendSmsCode(phoneNumber);
        return Result.judge(result);
    }
}

2.4 mobile terminal access SMS verification code authorization mode

There is a front-end framework for mobile mall app to use uni app cross platform applications. Because the mall has always been presented as one end of wechat applet, the power of uni app can not be reflected. Take this opportunity to extend the authorization mode of SMS verification code for mall app and add the login interface of SMS verification code for H5, Android and IOS.

Let's take a look at the different rendering effects of the mall app login interface in H5/Android/IOS and wechat applets.

H5/Android/IOS login interface

Wechat applet login interface

The login page / pages/login/login.vue has different presentation on different platforms. The implementation principle is realized through #ifdef MP and #ifndef MP conditional compilation instructions, where #ifdef MP is compiled and effective on the applet platform, and #ifdef MP is compiled and effective on the non applet platform.

During development and compilation, when you click Run on the HBuilderX toolbar and select different platforms, different pages will be presented.

  1. Run → run to the built-in browser → SMS verification code login interface;
  2. Run → run to applet simulator → wechat developer tool → applet authorization login interface;

When it comes to accessing the SMS verification code of Spring Security OAuth2 extension, it is important to see how to transmit parameters. In the / api/user.js code of the mall app:

Copy code 123456789101121314151617 JAVASCRIPT// H5/Android/IOS SMS verification code login
// #ifndef MP
export function login( mobile,code) {
	return request({
		url: '/youlai-auth/oauth/token',
		method: 'post',
		params: {
			mobile: mobile,
			code: code,
			grant_type: 'sms_code'
		},
		headers: {
			'Authorization': 'Basic bWFsbC1hcHA6MTIzNDU2' // Client information Base64 encryption, plaintext: Mall app: 123456
		}
	})
}
// #endif

Give the mall app client support for sms_code mode

3. Test

At this point, the authorization mode for H5/Android/IOS mobile terminal to access the SMS verification code extended by Spring Security OAuth2 has been completed. The next extended authorization mode is for the authorized login of the mobile terminal of the hottest wechat applet.

4, Wechat authorization mode

1. Principle

The login authorization flow chart of wechat applet is as follows. Our role is the developer server. Our main work is to receive the code of the applet and obtain the openid and session from the wechat server_ After the key, the developer server generates a session (token) and returns it to the applet. The subsequent applet carries the token to interact with the developer server, so there is no wechat server.

The principle of Spring Security OAuth2 wechat authorization extension is the same as the above SMS verification code. Add the authorizer WechatTokenGranter to build WechatAuthenticationToken and match it to the authentication provider
WechatAuthenticationProvider completes the authentication authorization logic in its authenticate method.

2. Actual combat

2.1 wechat authorization mode extension

WechatTokenGranter

WechatTokenGranter wechat authorizer receives code, encryptedData and iv to build WechatAuthenticationToken

Copy code 123456789101121314151617181920212223242526272829303313233435363738394041424344454647484950515253 JAVA/**
 *  Wechat authorizer
 *
 * @author <a href="mailto:xianrui0365@163.com">xianrui</a>
 * @date 2021/9/25
 */
public class WechatTokenGranter extends AbstractTokenGranter {

    /**
     * wechat authorizer CaptchaTokenGranter supports authorization mode
     * Pass the value grant according to the interface_ The value of type = wechat matches this authorizer
     * See the following two methods for matching logic
     *
     * @see org.springframework.security.oauth2.provider.CompositeTokenGranter#grant(String, TokenRequest)
     * @see org.springframework.security.oauth2.provider.token.AbstractTokenGranter#grant(String, TokenRequest)
     */
    private static final String GRANT_TYPE = "wechat";
    private final AuthenticationManager authenticationManager;

    public WechatTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, AuthenticationManager authenticationManager) {
        super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
        this.authenticationManager = authenticationManager;
    }

    @Override
    protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {

        Map<String, String> parameters = new LinkedHashMap(tokenRequest.getRequestParameters());
        String code = parameters.get("code");
        String encryptedData = parameters.get("encryptedData");
        String iv = parameters.get("iv");

        parameters.remove("code");
        parameters.remove("encryptedData");
        parameters.remove("iv");

        Authentication userAuth = new WechatAuthenticationToken(code, encryptedData,iv); // Unauthenticated status
        ((AbstractAuthenticationToken) userAuth).setDetails(parameters);

        try {
            userAuth = this.authenticationManager.authenticate(userAuth); // Under certification
        } catch (Exception e) {
            throw new InvalidGrantException(e.getMessage());
        }

        if (userAuth != null && userAuth.isAuthenticated()) { // Authentication successful
            OAuth2Request storedOAuth2Request = this.getRequestFactory().createOAuth2Request(client, tokenRequest);
            return new OAuth2Authentication(storedOAuth2Request, userAuth);
        } else { // Authentication failed
            throw new InvalidGrantException("Could not authenticate code: " + code);
        }
    }
}

WechatAuthenticationProvider

Finally, the authentication logic is completed in the authenticate() method of the wechat authentication provider, and the token is returned successfully.

Copy code 12345678910112131415161718192021222324252627282930331323343536373839404142434445464748495051525354555657585960 JAVA/**
 * Wechat authentication provider
 *
 * @author <a href="mailto:xianrui0365@163.com">xianrui</a>
 * @date 2021/9/25
 */
@Data
public class WechatAuthenticationProvider implements AuthenticationProvider {

    private UserDetailsService userDetailsService;
    private WxMaService wxMaService;
    private MemberFeignClient memberFeignClient;

    /**
     * Wechat authentication
     *
     * @param authentication
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        WechatAuthenticationToken authenticationToken = (WechatAuthenticationToken) authentication;
        String code = (String) authenticationToken.getPrincipal();

        WxMaJscode2SessionResult sessionInfo = null;
        try {
            sessionInfo = wxMaService.getUserService().getSessionInfo(code);
        } catch (WxErrorException e) {
            e.printStackTrace();
        }
        String openid = sessionInfo.getOpenid();
        Result<MemberAuthDTO> memberAuthResult = memberFeignClient.loadUserByOpenId(openid);
        // Wechat user does not exist, register as a new member
        if (memberAuthResult != null && ResultCode.USER_NOT_EXIST.getCode().equals(memberAuthResult.getCode())) {

            String sessionKey = sessionInfo.getSessionKey();
            String encryptedData = authenticationToken.getEncryptedData();
            String iv = authenticationToken.getIv();
            // Decrypt encryptedData to obtain user information
            WxMaUserInfo userInfo = wxMaService.getUserService().getUserInfo(sessionKey, encryptedData, iv);

            UmsMember member = new UmsMember();
            BeanUtil.copyProperties(userInfo, member);
            member.setOpenid(openid);
            member.setStatus(GlobalConstants.STATUS_YES);
            memberFeignClient.add(member);
        }
        UserDetails userDetails = ((MemberUserDetailsServiceImpl) userDetailsService).loadUserByOpenId(openid);
        WechatAuthenticationToken result = new WechatAuthenticationToken(userDetails, new HashSet<>());
        result.setDetails(authentication.getDetails());
        return result;
    }


    @Override
    public boolean supports(Class<?> authentication) {
        return WechatAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

2.2 wechat applet access wechat authorization mode

Also in the interface file of the mall app, / api/user.js, let's first look at how the applet side transfers values?

Copy code 12345678910112131415161718 JAVASCRIPT// Applet authorized login
// #ifdef MP
export function login(code, encryptedData,iv) {
	return request({
		url: '/youlai-auth/oauth/token',
		method: 'post',
		params: {
			code: code,
			encryptedData: encryptedData,
			iv:iv,
			grant_type: 'wechat'
		},
		headers: {
			'Authorization': 'Basic bWFsbC13ZWFwcDoxMjM0NTY=' // Client information Base64 encryption, plaintext: Mall Web: 123456
		}
	})
}
// #endif

Set the OAuth2 client to support wechat authorization mode

3. Test

At this point, the wechat authorization extension has been completed, and the three authorization modes commonly used in the actual business scenario have come to an end.

However, if you know something about Spring Security OAuth2, you may wonder whether the refresh mode corresponding to these extended modes needs to be adjusted?

If the extension is only for a user system and an authentication method (user name / mobile number / openid), such as the extension of verification code mode, there is no need to adjust the refresh mode.

However, if it is a multi-user system or multiple authentication methods, Youlai mall is a multi-user system and multiple authentication methods. At this time, you must make some adjustments to adapt, but the changes are not big. Why and how to adjust are described in detail below.

5, Multi user system refresh mode

1. Principle

The timing chart of the refresh mode is as follows. Compared with the password mode, it is only the change of Granter and Provider.

Focus on the authentication provider for refresh mode
PreAuthenticatedAuthenticationProvider. Its authenticate() authentication method only performs user status verification, and the check() method is called
AccountStatusUserDetailsChecker#check(UserDetails).

Pay attention
this.preAuthenticatedUserDetailsService.loadUserDetails((PreAuthenticatedAuthenticationToken)authentication); of   Preauthenticateduserdetailsservice user service.

When the authorization mode is not extended, it is set as follows

Then in
Authorization server endpoints configurer #adduserdetailsservice (defaulttoken services, userdetailsservice) construct   UserDetailService user service is set in PreAuthenticatedAuthenticationProvider.

In this way, the problem can be imagined under multi-user system authentication. Users include system users and member users respectively. It is certainly impossible to fix a user service here. When creating a Provider in the extended authorization mode, you can specify a specific user service UserDetailService, as follows:

You can add a corresponding refresh mode for each authorization mode extension, but it is troublesome. The core diagram of the implementation scheme in this paper is simple and effective, so the other scheme used here is to reset
Of PreAuthenticatedAuthenticationProvider   The preAuthenticatedUserDetailsService property enables it to judge and select the user system and authentication method.

2. Actual combat

Firstly, we know that an OAuth2 client basically corresponds to a user system. For example, the corresponding relationship between the client and the user system of Youlai mall project is as follows:

OAuth2 client name

OAuth2 client ID

User system

management system

mall-admin-web

System user

H5/Android/IOS mobile terminal

mall-app

Mall member

Applet side

mall-weapp

Mall member

There is a very simple and effective idea. You can maintain a mapping relationship Map in the system as shown in the above table, and then select the user system according to the transmitted client ID.

That's it? Of course not. There is another point you must consider. For example, although the user system of the mobile terminal is a member user, it may have a variety of authentication methods, such as SMS authentication code, user name and password, and even more authentication methods.

The default UserDetailsService interface of Spring Security OAuth2 has only one loadUserByUsername() method, which obviously cannot support multiple authentication methods.

Copy code 123 JAVApublic interface UserDetailsService {
    UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}

Therefore, you need to add an authentication method in the implementation class of UserDetailsService, and then convert UserDetailsService into a specific implementation class at runtime. For details, see the following items
The implementation of MemberUserDetailsServiceImpl supports both mobile phone number and three-party ID openid to obtain user authentication information, that is, two different authentication methods.

Copy code 12345678910112131415161718192021222324252627282930333435373839404142434445464748495051525354555657575859061626364656667686970717273 JAVA/**
 * Mall member user authentication service
 *
 * @author <a href="mailto:xianrui0365@163.com">xianrui</a>
 */
@Service("memberUserDetailsService")
@RequiredArgsConstructor
public class MemberUserDetailsServiceImpl implements UserDetailsService {

    private final MemberFeignClient memberFeignClient;

    @Override
    public UserDetails loadUserByUsername(String username) {
        return null;
    }

    /**
     * Mobile phone number authentication method
     *
     * @param mobile
     * @return
     */
    public UserDetails loadUserByMobile(String mobile) {
        MemberUserDetails userDetails = null;
        Result<MemberAuthDTO> result = memberFeignClient.loadUserByMobile(mobile);
        if (Result.isSuccess(result)) {
            MemberAuthDTO member = result.getData();
            if (null != member) {
                userDetails = new MemberUserDetails(member);
                userDetails.setAuthenticationMethod(AuthenticationMethodEnum.MOBILE.getValue());   // Authentication method: OpenId
            }
        }
        if (userDetails == null) {
            throw new UsernameNotFoundException(ResultCode.USER_NOT_EXIST.getMsg());
        } else if (!userDetails.isEnabled()) {
            throw new DisabledException("The account has been disabled!");
        } else if (!userDetails.isAccountNonLocked()) {
            throw new LockedException("The account has been locked!");
        } else if (!userDetails.isAccountNonExpired()) {
            throw new AccountExpiredException("This account has expired!");
        }
        return userDetails;
    }


    /**
     * openid Authentication mode
     *
     * @param openId
     * @return
     */
    public UserDetails loadUserByOpenId(String openId) {
        MemberUserDetails userDetails = null;
        Result<MemberAuthDTO> result = memberFeignClient.loadUserByOpenId(openId);
        if (Result.isSuccess(result)) {
            MemberAuthDTO member = result.getData();
            if (null != member) {
                userDetails = new MemberUserDetails(member);
                userDetails.setAuthenticationMethod(AuthenticationMethodEnum.OPENID.getValue());   // Authentication method: OpenId
            }
        }
        if (userDetails == null) {
            throw new UsernameNotFoundException(ResultCode.USER_NOT_EXIST.getMsg());
        } else if (!userDetails.isEnabled()) {
            throw new DisabledException("The account has been disabled!");
        } else if (!userDetails.isAccountNonLocked()) {
            throw new LockedException("The account has been locked!");
        } else if (!userDetails.isAccountNonExpired()) {
            throw new AccountExpiredException("This account has expired!");
        }
        return userDetails;
    }
}

New
PreAuthenticatedUserDetailsService can select UserDetailService and method to obtain user information UserDetail according to the client and authentication method

Copy code 12345678910112131415161718192021222324252627282930331323353637383940414243444546474849505152535455565757585906162636465666768 JAVA/**
 * Refresh the token and authenticate UserDetailsService again
 *
 * @author <a href="mailto:xianrui0365@163.com">xianrui</a>
 * @date 2021/10/2
 */
@NoArgsConstructor
public class PreAuthenticatedUserDetailsService<T extends Authentication> implements AuthenticationUserDetailsService<T>, InitializingBean {

    /**
     * Mapping of client ID and user service UserDetailService
     *
     * @see com.youlai.auth.security.config.AuthorizationServerConfig#tokenServices(AuthorizationServerEndpointsConfigurer)
     */
    private Map<String, UserDetailsService> userDetailsServiceMap;

    public PreAuthenticatedUserDetailsService(Map<String, UserDetailsService> userDetailsServiceMap) {
        Assert.notNull(userDetailsServiceMap, "userDetailsService cannot be null.");
        this.userDetailsServiceMap = userDetailsServiceMap;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(this.userDetailsServiceMap, "UserDetailsService must be set");
    }

    /**
     * Override the preAuthenticatedUserDetailsService property of PreAuthenticatedAuthenticationProvider. You can select user service UserDetailService to obtain user information UserDetail according to the client and authentication method
     *
     * @param authentication
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserDetails(T authentication) throws UsernameNotFoundException {
        String clientId = RequestUtils.getOAuth2ClientId();
        // Obtain the authentication method. The default is username
        AuthenticationMethodEnum authenticationMethodEnum = AuthenticationMethodEnum.getByValue(RequestUtils.getAuthenticationMethod());
        UserDetailsService userDetailsService = userDetailsServiceMap.get(clientId);
        if (clientId.equals(SecurityConstants.APP_CLIENT_ID)) {
            // The user system of the mobile terminal is a member, and the authentication method is mobile authentication through the mobile number
            MemberUserDetailsServiceImpl memberUserDetailsService = (MemberUserDetailsServiceImpl) userDetailsService;
            switch (authenticationMethodEnum) {
                case MOBILE:
                    return memberUserDetailsService.loadUserByMobile(authentication.getName());
                default:
                    return memberUserDetailsService.loadUserByUsername(authentication.getName());
            }
        } else if (clientId.equals(SecurityConstants.WEAPP_CLIENT_ID)) {
            // The user system of the applet is a member, and the authentication method is openid authentication through the wechat three-party logo
            MemberUserDetailsServiceImpl memberUserDetailsService = (MemberUserDetailsServiceImpl) userDetailsService;
            switch (authenticationMethodEnum) {
                case OPENID:
                    return memberUserDetailsService.loadUserByOpenId(authentication.getName());
                default:
                    return memberUserDetailsService.loadUserByUsername(authentication.getName());
            }
        } else if (clientId.equals(SecurityConstants.ADMIN_CLIENT_ID)) {
            // The user system of the management system is the system user, and the authentication method is authenticated by the user name username
            switch (authenticationMethodEnum) {
                default:
                    return userDetailsService.loadUserByUsername(authentication.getName());
            }
        } else {
            return userDetailsService.loadUserByUsername(authentication.getName());
        }
    }
}

AuthorizationServerConfig configuration reset
Of PreAuthenticatedAuthenticationProvider   preAuthenticatedUserDetailsService property value

Copy code 123456789101121314151617181920212223242526272829303313233536373839404142434445464748495051525354555657585906162636465666768697071 JAVA    /**
     * Configure authorization, access endpoint of token and token services
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        // Token enhancement
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> tokenEnhancers = new ArrayList<>();
        tokenEnhancers.add(tokenEnhancer());
        tokenEnhancers.add(jwtAccessTokenConverter());
        tokenEnhancerChain.setTokenEnhancers(tokenEnhancers);

        // Obtain the authorizer of the original default authorization mode (authorization code mode, password mode, client mode and simplified mode)
        List<TokenGranter> granterList = new ArrayList<>(Arrays.asList(endpoints.getTokenGranter()));

        // Add verification code authorization mode authorizer
        granterList.add(new CaptchaTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(),
                endpoints.getOAuth2RequestFactory(), authenticationManager, stringRedisTemplate
        ));

        // Add the authorizer of SMS verification code authorization mode
        granterList.add(new SmsCodeTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(),
                endpoints.getOAuth2RequestFactory(), authenticationManager
        ));

        // Add the authorizer of wechat authorization mode
        granterList.add(new WechatTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(),
                endpoints.getOAuth2RequestFactory(), authenticationManager
        ));

        CompositeTokenGranter compositeTokenGranter = new CompositeTokenGranter(granterList);
        endpoints
                .authenticationManager(authenticationManager)
                .accessTokenConverter(jwtAccessTokenConverter())
                .tokenEnhancer(tokenEnhancerChain)
                .tokenGranter(compositeTokenGranter)
                /** refresh token There are two usage methods: reuse (true) and non reuse (false). The default is true
                 *  1 Reuse: when the access token is expired and refreshed, the expiration time of the refresh token remains unchanged, and the time of initial generation still prevails
                 *  2 Non reuse: when the access token expires and refreshes, the refresh token expires. Within the validity period of the refresh token, the refresh will never expire, so that there is no need to log in again
                 */
                .reuseRefreshTokens(true)
                .tokenServices(tokenServices(endpoints))
        ;
    }


    public DefaultTokenServices tokenServices(AuthorizationServerEndpointsConfigurer endpoints) {
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> tokenEnhancers = new ArrayList<>();
        tokenEnhancers.add(tokenEnhancer());
        tokenEnhancers.add(jwtAccessTokenConverter());
        tokenEnhancerChain.setTokenEnhancers(tokenEnhancers);

        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenStore(endpoints.getTokenStore());
        tokenServices.setSupportRefreshToken(true);
        tokenServices.setClientDetailsService(clientDetailsService);
        tokenServices.setTokenEnhancer(tokenEnhancerChain);

        // In the multi-user system, refresh the token to re authenticate the mapping Map of the client ID and UserDetailService
        Map<String, UserDetailsService> clientUserDetailsServiceMap = new HashMap<>();
        clientUserDetailsServiceMap.put(SecurityConstants.ADMIN_CLIENT_ID, sysUserDetailsService); // Management system client
        clientUserDetailsServiceMap.put(SecurityConstants.APP_CLIENT_ID, memberUserDetailsService); // Android/IOS/H5 mobile client
        clientUserDetailsServiceMap.put(SecurityConstants.WEAPP_CLIENT_ID, memberUserDetailsService); // Wechat applet client

        // Reset PreAuthenticatedAuthenticationProvider#preAuthenticatedUserDetailsService to distinguish user systems and obtain authenticated user information according to client ID and authentication method
        PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
        provider.setPreAuthenticatedUserDetailsService(new PreAuthenticatedUserDetailsService<>(clientUserDetailsServiceMap));
        tokenServices.setAuthenticationManager(new ProviderManager(Arrays.asList(provider)));
        return tokenServices;
    }

The core code is basically above. After the above adjustments are completed, the refresh mode is OK. Next, test the refresh mode corresponding to the newly extended authorization mode one by one.

3. Test

3.1 operation instructions for importing cURL from postman

All the following tests will post cURL. Why do you emphasize this? Originally, I thought that I put the complete request screenshot of obtaining the token by testing Spring Security OAuth2 with Postman into the project description document README.md, so that no one would ask the login interface 403 to report an error, but the fact feedback was really disappointing, so that I basically chose to be silent when there were such questions later. I hope you can think and understand in a transposition. Therefore, the idea this time is to post the interface information in the form of cURL, and then directly import it into Postman test.

The following is the URL of the item to get the token

Copy code 12 SHELLcurl --location --request POST 'http://localhost:9999/youlai-auth/oauth/token?username=admin&password=123456&grant_type=password' \
--header 'Authorization: Basic bWFsbC1hZG1pbi13ZWI6MTIzNDU2'

Enter Postman and select File → Import → Raw text to Import the above cURL

3.2 password mode test

Client information used for password mode test, client ID: client key: Mall admin Web: 123456 ----- Base64 online coding →
bWFsbC1hZG1pbi13ZWI6MTIzNDU2

If you want to change the client, please change the client information in the request header Authorization of the interface below, otherwise you will be prompted with 403 because your client information is incorrect and authentication is unsuccessful. Access is prohibited.

Some people will ask that there are projects that do not customize the handling of client authentication exceptions. In fact, I provided solutions in my previous articles
https://www.cnblogs.com/haoxianrui/p/14028366.html#3 -The client authentication is abnormal. If necessary, it can be adjusted according to the article. As for why there is no solution in the project, first of all, I think the implementation is more complex. If you have a good solution, you are welcome to put forward it. In addition, this kind of client information error can be avoided as a developer.

Get token

Copy code 12 SHELLcurl --location --request POST 'http://localhost:9999/youlai-auth/oauth/token?username=admin&password=123456&grant_type=password' \
--header 'Authorization: Basic bWFsbC1hZG1pbi13ZWI6MTIzNDU2'

Refresh token

refresh_ The token needs to be replaced. In the first step, get the refresh returned by the token_ token

Copy code 12 SHELLcurl --location --request POST 'http://localhost:9999/youlai-auth/oauth/token?refresh_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJhdGkiOiJmYzdiOGNhZi1iNmI4LTRlZTEtOGE4OC0yYzdmZTcxNTA0YjEiLCJleHAiOjE2MzQ0NDg5NDIsInVzZXJJZCI6MiwiYXV0aG9yaXRpZXMiOlsiQURNSU4iXSwianRpIjoiOGU3ZWE5MjAtOGQ0Ni00NmFlLWI3ODYtZTc3ZjAxY2Y5ZjIyIiwiY2xpZW50X2lkIjoibWFsbC1hZG1pbi13ZWIiLCJ1c2VybmFtZSI6ImFkbWluIn0.I_9uLpr7WUeb-JNSBr17Ya59qP3a8EFSps3MwqpTS-mlDldx-HDsJM41Pl11-b_99_yhl_h-FRhIYpGaOqP4p7428z_LQmlpBrebx9TVcSk_gVbDPjN3Q2glxaupvCGmAuRNWby0Aam-On2wO8RkKKhH0arI2nf4rseu18WN0-cqxJuYn10hyQ-T7n5n3zjnx92nMyqESWqfPqsy8_eie-can4113PBHhnqs9QI1SQ-1Z_AtZLgAb1FzaV2JuTqqbPlVULM-uaQnIoe0zNq5R-TYoUJ2cQNkP4YOR4e9TP26iSPLNlcsg59TFHi0UhrZiZqvS3i5nUkqV0jpzvYVrg&grant_type=refresh_token' \
--header 'Authorization: Basic bWFsbC1hZG1pbi13ZWI6MTIzNDU2' 

3.3 verification code mode test

The verification code mode test uses the client information, client ID: client key: Mall admin Web: 123456 ----- Base64 online coding →
bWFsbC1hZG1pbi13ZWI6MTIzNDU2

Get token

Copy code 12 SHELLcurl --location --request POST 'http://localhost:9999/youlai-auth/oauth/token?username=admin&password=123456&grant_type=captcha&uuid=11add22b38e74a57bade0bf628a70645&validateCode=1' \
--header 'Authorization: Basic bWFsbC1hZG1pbi13ZWI6MTIzNDU2

Refresh token

Copy code 12 SHELLcurl --location --request POST 'http://localhost:9999/youlai-auth/oauth/token?refresh_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJhdGkiOiJiMTU5ZGU2Ni1iYmY5LTRmOWEtYTg1MC1kMjk1MDJiYTNjY2IiLCJleHAiOjE2MzQ0NjQxNjUsInVzZXJJZCI6MiwiYXV0aG9yaXRpZXMiOlsiQURNSU4iXSwianRpIjoiN2MwNDk2YzgtMTRjMC00MWJhLTk2OTUtYTk2ZGYwODQ1NGMxIiwiY2xpZW50X2lkIjoibWFsbC1hZG1pbi13ZWIiLCJ1c2VybmFtZSI6ImFkbWluIn0.j3n1FrMEIRkb_-3YhoDdPA4qBofzjD4y6HWdhCRdIjWU3D1La9ee_guhdeEEL49sfdHQSek_T4funyUCegTCdxfowzh3JghtCXFyRdxSWxjgJalgSIGVcOSEePxADwf2biHB3m6WzpOT9FxEdBavT7mfdQRjfc276uL7zzi5blKc4pUzX9l1AvReMP7azT_6soBNi-nid5maUCpMx_w9AVUvjVl4L7QMCO22zEogs2SlpMpggAITMv3QKYYTZ3vzxL2oNR_r-9qXqN7W6DxGqQc1gIqXADX1oqsXzD4AaAtLqOslP8FM6HiOzzZVd1kmv1cPHzVzabx6vYUZFA1PMg&grant_type=refresh_token' \
--header 'Authorization: Basic bWFsbC1hZG1pbi13ZWI6MTIzNDU2'

3.4 SMS verification code test

Information of the client used for SMS verification code mode test, client ID: client key: Mall app: 123456 ----- Base64 online coding →
bWFsbC1hZG1pbi13ZWI6MTIzNDU2

Get token

Copy code 12 SHELLcurl --location --request POST 'http://localhost:9999/youlai-auth/oauth/token?mobile=17621590365&code=666666&grant_type=sms_code' \
--header 'Authorization: Basic bWFsbC1hcHA6MTIzNDU2'

Refresh token

Copy code 12 SHELLcurl --location --request POST 'http://localhost:9999/youlai-auth/oauth/token?refresh_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRoZW50aWNhdGlvbk1ldGhvZCI6Im1vYmlsZSIsInVzZXJfbmFtZSI6IjE3NjIxNTkwMzY1Iiwic2NvcGUiOlsiYWxsIl0sImF0aSI6IjBlZGMyZjI0LWFiNWUtNDkxYy1iYjAyLTdlOWJkN2U5M2Y0MiIsImV4cCI6MTYzNDQ2NTEzMCwidXNlcklkIjo1OSwianRpIjoiZjcyMWZhZjAtZTczMS00MmUxLTgxYjAtMjg4NDEwZjQzODA0IiwiY2xpZW50X2lkIjoibWFsbC1hcHAiLCJ1c2VybmFtZSI6IjE3NjIxNTkwMzY1In0.RdtJiNhk3OheoUcpUtM9JBgwLfSt1k3FhEvgMYeDSFwf28TeS_SF2LY7vzOrbJfYQZuaMzvMfoSljeDuQoBr38Ebh2LogbZClaDY72TO9P88DAW-1l2Rjm1XYFMEzCZYweDehT2tJU6eOwN8GZ40dzcCnqjZwgCKgoIdJksxMB6n96Kfmxw_Z3TUny5j2mdDZB79bwWci86jev6y-RUTjbZWRu1vH4MVJ0hCOCRARoem1jlkW6nnkzhE84OasDI9RCg5jsA_ZNs3x-rFNnRY7T5gQOAOwPvJKVcXww35BGYZGHCHqQb6QEbxul6Pg1rLjFU6YgsSO1Xq_cWVOt0Nvg&grant_type=refresh_token' \
--header 'Authorization: Basic bWFsbC1hcHA6MTIzNDU2'

3.5 wechat authorization mode test

Information of the client used in wechat authorization mode test, client ID: client key: Mall Web: 123456 ----- Base64 online coding → bWFsbC13ZWFwcDoxMjM0NTY=

Get token

Copy code 12 SHELLcurl --location --request POST 'http://localhost:9999/youlai-auth/oauth/token?code=063hEOFa1N1dWB0XpRIa1WvNw74hEOF-&encryptedData=1qmFeCKbTxZyCdzctu37sX+jOnM9dZG9lKyD3v6FhA5sCEtDwaF/wqyVR70QVrqt7bGVH+Kb+PBsFJlBXUdjnFGlrwmPqgNusI4f5eA8SvZgopvmlzJhXwe+OjLCQooeGnSkcnUrUuMA/G4ZYWFeljaHhxJq/75APWs4HyLANfbeLp50qI9xrRJVUXlTqdqJ0ub38ZxWVvWZMqY8FaskAiZpxzrF30eXu93BCpDavRCVzlSfv6LFJmmvEGVOKr4Wap9ND82N3sDMyArRsdhdhmoWIYBbRs+iLbKcS4WyOhpmaQr4fhhOuxO+zSAa7W+eNmCH2Id6Pgpvhl6ureNNzEb0cQLoksP6oakPmv/yEiw5fnW6Oi9jJbxzlMyORN3/atHgBl6zLIgS9UMhFE+42Vp5B3L8jLly4+B4NpNgol+khXoh+ycUXSRPV4bUuriv&iv=j+brWSrqRW+d4lAjRWW4RA==&grant_type=wechat' \
--header 'Authorization: Basic bWFsbC13ZWFwcDoxMjM0NTY='

Refresh token

6, Summary

Based on Spring Security OAuth2, this paper extends the commonly used verification code mode, SMS verification code mode and wechat authorization mode, and applies them to the management front end, mobile application end and wechat applet end of Youlai mall respectively. At the same time, slightly adjust the refresh mode to adapt to several extended modes and multi-user system. Through the extension of authorization mode, the authentication process and underlying principle of Spring Security OAuth2 are exposed. It is believed that after having a clear idea of the process and principle, different authentication requirements can be handy. Finally, I still sigh the charm of the Spring framework, that is, you can feel that it will leave you an extension entrance on the basis of function implementation, rather than make you want to change its source code to implement. Finally, I hope everyone can harvest something. Although we don't want to do anything here, and it's not good for us to write these to tell the truth, after all, it took more than half a month to write this article. It's my own effort, and I don't want it to be wasted.

Tags: Java JavaEE Back-end Interview

Posted on Fri, 03 Dec 2021 11:18:27 -0500 by MasterACE14