作者归档:Rick

sharp-database中的BaseDAOImpl实体中自定义属性序列化

环境搭建

属性类

PhoneNumber

@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PhoneNumber {

    private String code;

    private String number;

    /**
     * 序列化格式
     * @return
     */
    @Override
    public String toString() {
        return code + "-" + number;
    }
}

实体类

@Data
@ToString
@Builder
@TableName("t_project")
@NoArgsConstructor
@AllArgsConstructor
public class Project {
    @Id
    private Long id;

    private PhoneNumber phoneNumber;

反序列化转换

@Component
final public class StringToPhoneNumberConverterFactory implements ConverterFactory<String, PhoneNumber> {

    @Override
    public <T extends PhoneNumber> Converter<String, T> getConverter(Class<T> aClass) {
        return new StringToPhoneNumber();
    }

    private static class StringToPhoneNumber<T> implements Converter<String, T> {

        @Override
        public T convert(String source) {
            if (StringUtils.isBlank(source)) {
                return null;
            }

            String[] arr = source.split("-");
            return (T) PhoneNumber.builder().code(arr[0]).number(arr[1]).build();
        }
    }

}

测试

@Test
public void testSave() {
    Project project = new Project();

    project.setPhoneNumber("861-18888888888");

    projectDAO.insert(project);
    Assert.assertNotNull(project.getId());
}

@Test
public void testFindById() {
    Optional<Project> optional = projectDAO.selectById(479411923147194368L);
    Project project = optional.get();

    Assert.assertEquals("18888888888", project.getPhoneNumber().getNumber());
}

sharp-database中的BaseDAOImpl实体中对Enum属性的支持

环境搭建

枚举类

StatusEnum code是String类型

@AllArgsConstructor
@Getter
public enum StatusEnum {
    DEFAULT("DEFAULT");

    @JsonValue
    public String getCode() {
        return this.name();
    }

    private final String label;

    public static StatusEnum valueOfCode(String code) {
        return valueOf(code);
    }
}

SexEnum code是int类型

@AllArgsConstructor
@Getter
public enum SexEnum {
    UNKNOWN(0, "Unknown"),
    MALE(1, "Male"),
    FEMALE(2, "Female");

    private static final Map<Integer, SexEnum> codeMap = new HashMap<>();

    static {
        for (SexEnum e : values()) {
            codeMap.put(e.code, e);
        }
    }

    private final int code;

    private final String label;

    @JsonValue
    public int getCode() {
        return this.code;
    }

    public static SexEnum valueOfCode(int code) {
        return codeMap.get(code);
    }
}

枚举中必须包含静态方法 valueOfCode 和 方法 getCode

实体类

@Data
@ToString
@Builder
@TableName("t_project")
@NoArgsConstructor
@AllArgsConstructor
public class Project {
    @Id
    private Long id;

    private SexEnum sex;

    private StatusEnum status;

测试

@Test
public void testSave() {
    Project project = new Project();

    project.setSex(SexEnum.FEMALE);
    project.setStatus(StatusEnum.DEFAULT)
    projectDAO.insert(project);
    Assert.assertNotNull(project.getId());
}

@Test
public void testFindById() {
    Optional<Project> optional = projectDAO.selectById(479411923147194368L);
    Project project = optional.get();

    Assert.assertEquals(SexEnum.FEMALE, project.getSex());
    Assert.assertEquals(SexEnum.DEFAULT project.getStatus());
}

sharp-database中的BaseDAOImpl实体中有POJO属性/List POJO/List Map使用Json存储

环境准备

Address.java

@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Address implements JsonStringToObjectConverterFactory.JsonValue {

    private String code;

    private String detail;

}
  • 实现接口 JsonStringToObjectConverterFactory.JsonValue 表示反序列化的时候,底层使用JsonUtils.toObject()

Project.java

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Project {

    @Id
    private Long id;

    private Address address;

    private List<Address> list;

}

测试

@Test
public void testSave() {
    Project project = new Project();

    project.setAddress(Address.builder().code("001").detail("苏州").build());
    project.setList(Lists.newArrayList(Address.builder().code("001").detail("苏州").build()));

    projectDAO.insert(project);
    Assert.assertNotNull(project.getId());
}

@Test
 public void testFindById() {
     Optional<Project> optional = projectDAO.selectById(479411923147194368L);
     Project project = optional.get();
     Assert.assertEquals("001", project.getAddress().getCode());
     Assert.assertEquals("001", project.getList().get(0).getCode());
}

Spring Security Oauth2 resource-server鉴权(七)

说明

spring-security-oauth 这个项目不赞成使用了。oauth2已经由Spring Security提供服务。Spring Security没有提供对认证服务器的支持,需要 spring-authorization-server 去支持。
https://spring.io/blog/2020/04/15/announcing-the-spring-authorization-server

The Spring Security OAuth project is deprecated. The latest OAuth 2.0 support is provided by Spring Security. See the OAuth 2.0 Migration Guide for further details.

Since Spring Security doesn’t provide Authorization Server support, migrating a Spring Security OAuth Authorization Server see https://spring.io/blog/2020/04/15/announcing-the-spring-authorization-server

配置

添加依赖 build.gradle

implementation "org.springframework.boot:spring-boot-starter-oauth2-resource-server"
implementation 'org.springframework.boot:spring-boot-starter-web'

配置文件 application.yml

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          public-key-location: classpath:key.public
          jws-algorithm: RS512

配置类 ResourceServerConfig.java

@EnableWebSecurity
public class ResourceServerConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests(a -> a
                    .antMatchers("/public", "/error", "/webjars/**").permitAll()
                    .antMatchers("/admin").hasAnyRole("admin")
                    .anyRequest().authenticated()
            )
            .oauth2ResourceServer()
            .jwt();
    }

}
  • /public 是不需要认证就可以访问的
  • /admin 是需要admin角色才能访问的
  • /user 是需要user角色才能访问的
  • /index 是需要认证的

工具类

生成私钥和公钥

@UtilityClass
@Slf4j
public class SecurityUtils {

    /**
     * 私钥
     */
    private static final RSAPrivateKey PRIVATE_KEY = RsaKeyConverters.pkcs8().convert(SecurityUtils.class.getResourceAsStream("/key.private"));

    /**
     * 公钥
     */
    private static final RSAPublicKey PUBLIC_KEY = RsaKeyConverters.x509().convert(SecurityUtils.class.getResourceAsStream("/key.public"));


    /**
     * rsa算法加解密时的填充方式
     */
    private static final String RSA_PADDING = "RSA/ECB/PKCS1Padding";

    /**
     * 生成私钥和公钥
     */
    public static void keys() {
        try {
            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
            keyPairGen.initialize(2048);
            KeyPair keyPair = keyPairGen.generateKeyPair();
            PrivateKey privateKey = keyPair.getPrivate();
            PublicKey publicKey = keyPair.getPublic();
            log.info("{}{}{}", "\n-----BEGIN PRIVATE KEY-----\n", Base64.getMimeEncoder().encodeToString(privateKey.getEncoded()), "\n-----END PRIVATE KEY-----");
            log.info("{}{}{}", "\n-----BEGIN PUBLIC KEY-----\n", Base64.getMimeEncoder().encodeToString(publicKey.getEncoded()), "\n-----END PUBLIC KEY-----");
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * 加密
     *
     * @param plaintext 明文
     * @return 密文
     */
    private static String encrypt(String plaintext) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_PADDING);
            cipher.init(Cipher.ENCRYPT_MODE, PUBLIC_KEY);
            String encrypt = Base64.getEncoder().encodeToString(cipher.doFinal(plaintext.getBytes()));
            log.info("The plaintext {} is encrypted as: {}", plaintext, encrypt);
            return encrypt;
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * 解密
     *
     * @param cipherText 密文
     * @return 明文
     */
    private static String decrypt(String cipherText) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_PADDING);
            cipher.init(Cipher.DECRYPT_MODE, PRIVATE_KEY);
            String decrypt = new String(cipher.doFinal(Base64.getDecoder().decode(cipherText)));
            log.info("The ciphertext {} is decrypted as: {}", cipherText, decrypt);
            return decrypt;
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }

    public static void main(String[] args) {
        keys();
    }
}

将文件 key.privatekey.public 放到resources目录下

生成token

public final class JwtUtils {

    /**
     * 私钥
     */
    private static final RSAPrivateKey PRIVATE_KEY = RsaKeyConverters.pkcs8().convert(JwtUtils.class.getResourceAsStream("/key.private"));


    private JwtUtils() {}

    /**
     * 生成jwt
     *
     * @return jwt
     */
    public static String jwt(JWTClaimsSet claimsSet) {
        try {
            SignedJWT jwt = new SignedJWT(new JWSHeader(new JWSAlgorithm("RS512")), claimsSet);
            // 私钥签名,公钥验签
            jwt.sign(new RSASSASigner(PRIVATE_KEY));
            return jwt.serialize();
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }

    public static void main(String[] args) {
        JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
                .subject("RS512 Rick")
                .issuer("https://xhope.top")
                .claim("scope", "user")
                .build();

        String jwtToken = JwtUtils.jwt(claimsSet);
        System.out.println(jwtToken);
    }

}

测试接口

@RestController
public class ResourceController {

    /**
     * 403没有访问权限
     * @return
     */
    @GetMapping(value = "admin")
    public String admin() {
        return "admin";
    }

    @PreAuthorize("hasAnyAuthority('ROLE_USER, SCOPE_user')")
    @GetMapping(value = "user")
    public String user() {
        return "user";
    }

    /**
     * 不需要认证就能访问
     * @return
     */
    @GetMapping(value = "public")
    public String publicFun() {
        return "public";
    }
}

利用 postman 进行测试,将token放到header中

curl -X GET \
  http://localhost:8080/admin \
  -H 'Authorization: Bearer eyJhbGciOiJSUzUxMiJ9.eyJpc3MiOiJodHRwczpcL1wveGhvcGUudG9wIiwic3ViIjoiUlM1MTIgUmljayIsInNjb3BlIjoibWVzc2FnZS5yZWFkIG1lc3NhZ2Uud3JpdGUifQ.buy_qLLpLodfEwKwRatnHZctZv7pYrgaiX7gjC79tA5ZQiEI_zpO7IvPE_Pw3CSBBZ7Jfz90y1gIq85RK8pAVbIceARsvVK2t8wGq5N6L6jwmi9drkvEMEIdxIijVYfNH7EXakAqx3aN8siScXWX4VTYaSuSd0LFrzQiV2HDmBd0FMGH2OXJmebnD2HI-zXtp02isUTVLReF13DZWV4cG_sr2aix0BjkSl6fhXu7SLZnJTE0yHI47Sc68O6w6J5rqpYUfD4WtM_C9go3iyzldN4oVh67HvzEaJ62ZIx2sKjTITLE_quISxYEnYc62oR1hL87JkGayi7JFl1Sl6o9BA'

如果通过参数传递token,需要修改

@Override
protected void configure(HttpSecurity http) throws Exception {
    DefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();
    // 允许参数access_token
    resolver.setAllowUriQueryParameter(true);

    http
        .authorizeRequests(a -> a
                .antMatchers("/public", "/error", "/webjars/**").permitAll()
                .antMatchers("/admin").hasAnyRole("admin")
                .anyRequest().authenticated()
        )
        .oauth2ResourceServer()
            .bearerTokenResolver(resolver)
        .jwt();
}

利用浏览器进行测试,将token放到参数access_token中

curl -X GET \
  'http://localhost:8080/user?access_token=eyJhbGciOiJSUzUxMiJ9.eyJpc3MiOiJodHRwczpcL1wveGhvcGUudG9wIiwic3ViIjoiUlM1MTIgUmljayIsInNjb3BlIjoibWVzc2FnZS5yZWFkIG1lc3NhZ2Uud3JpdGUifQ.buy_qLLpLodfEwKwRatnHZctZv7pYrgaiX7gjC79tA5ZQiEI_zpO7IvPE_Pw3CSBBZ7Jfz90y1gIq85RK8pAVbIceARsvVK2t8wGq5N6L6jwmi9drkvEMEIdxIijVYfNH7EXakAqx3aN8siScXWX4VTYaSuSd0LFrzQiV2HDmBd0FMGH2OXJmebnD2HI-zXtp02isUTVLReF13DZWV4cG_sr2aix0BjkSl6fhXu7SLZnJTE0yHI47Sc68O6w6J5rqpYUfD4WtM_C9go3iyzldN4oVh67HvzEaJ62ZIx2sKjTITLE_quISxYEnYc62oR1hL87JkGayi7JFl1Sl6o9BA'

源码分析

BearerTokenAuthenticationFilter.java

  • 获取token:从header中获取name是 Authorization 的值, 判断是否是以 Bearer 开头,如果是,那么解析出token值;否则如果允许参数传递token,则尝试从参数access_token中解析token。
  • 验证token:将值包装成 BearerTokenAuthenticationToken,交由 AuthenticationManager,最终由 JwtAuthenticationProvider 进行验证。
  • 底层解析token:NimbusJwtDecoder 进行decode;如果是 SignedJWT 使用公钥(yml中配置的公钥,项目启动的时候就会读取公钥信息)验签;验证成功后token验证由 DelegatingOAuth2TokenValidator 代理去进行其他验证。JwtTimestampValidator 验证日期是否过期。

OAuth2ResourceServerJwtConfiguration.java

参考阅读:

Spring Security Oauth2 client支持GitHub第三方登录(六)

说明

spring-security-oauth 这个项目不赞成使用了。oauth2已经由Spring Security提供服务。Spring Security没有提供对认证服务器的支持,需要 spring-authorization-server 去支持。
https://spring.io/blog/2020/04/15/announcing-the-spring-authorization-server

The Spring Security OAuth project is deprecated. The latest OAuth 2.0 support is provided by Spring Security. See the OAuth 2.0 Migration Guide for further details.

Since Spring Security doesn’t provide Authorization Server support, migrating a Spring Security OAuth Authorization Server see https://spring.io/blog/2020/04/15/announcing-the-spring-authorization-server

集成github第三方登录

添加依赖 build.gradle

implementation "org.springframework.boot:spring-boot-starter-oauth2-client"
implementation 'org.springframework.boot:spring-boot-starter-web'

配置文件 application.yml

spring:
  security:
    oauth2:
      client:
        registration:
          github:
            client-id: xxx
            client-secret: xxx
            authorization-grant-type: authorization_code
            # /login/oauth2/code/ 固定格式
            redirect-uri: "http://localhost:8080/login/oauth2/code/github"
            # 只有一个权限就是user
            scope: user
        provider:
          github:
            authorization-uri: https://github.com/login/oauth/authorize
            token-uri: https://github.com/login/oauth/access_token

前往OAuth Apps配置github client信息

配置类 OAuth2ClientSecurityConfig.java

@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled=true, prePostEnabled = true)
@Slf4j
public class OAuth2ClientSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests(a -> a
                        .antMatchers("/public", "/error", "/webjars/**").permitAll()
                        .antMatchers("/admin").hasAnyRole("admin")
                        .anyRequest().authenticated()
                )
                .exceptionHandling(e -> e
                                .authenticationEntryPoint((request, response, e1) -> {
                                    log.error("认证异常:{}", e1.getMessage());
                                    response.sendRedirect("oauth2/authorization/github");
                                })
                )
                .oauth2Login().successHandler(((request, response, authentication) -> {
                    log.error("认证成功");
            OAuth2AuthenticationToken oAuth2AuthenticationToken = (OAuth2AuthenticationToken)authentication;
                    log.info("{} 在github上的信息是:{}", oAuth2AuthenticationToken.getPrincipal().getName(),
                            oAuth2AuthenticationToken);
                }));
    }
}
  • /public 是不需要认证就可以访问的
  • /admin 是需要admin角色才能访问的
  • /user 是需要user角色才能访问的
  • /index 是需要认证的

@EnableGlobalMethodSecurity 注解允许在方法级别使用@PreAuthorize(“hasAnyAuthority(‘ROLE_USER, SCOPE_user’)”)

测试接口

@RestController
public class ResourceController {

    /**
     * 通过token,可以访问github上的其他资源
     * curl -X GET \
     *   https://api.github.com/user \
     *   -H 'Authorization: Bearer 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' \
     * @param authorizedClient
     * @return
     */
    @GetMapping(value = {"index", "/"})
    public String index(@RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient) {
        return "index token = " + authorizedClient.getAccessToken().getTokenValue();
    }

    /**
     * 403没有访问权限
     * @return
     */
    @GetMapping(value = "admin")
    public String admin() {
        return "admin";
    }

    @PreAuthorize("hasAnyAuthority('ROLE_USER, SCOPE_user')")
    @GetMapping(value = "user")
    public String user() {
        return "user";
    }

    /**
     * 不需要认证就能访问
     * @return
     */
    @GetMapping(value = "public")
    public String publicFun() {
        return "public";
    }
}

源码分析

  • OAuth2AuthorizationRequestRedirectFilter.java
private void sendRedirectForAuthorization(HttpServletRequest request, HttpServletResponse response, OAuth2AuthorizationRequest authorizationRequest) throws IOException {
    if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorizationRequest.getGrantType())) {
        this.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, response);
    }

    this.authorizationRedirectStrategy.sendRedirect(request, response, authorizationRequest.getAuthorizationRequestUri());
}

拦截器组装数据后跳转以下URL

https://github.com/login/oauth/authorize?response_type=code&client_id=&redirect_uri=http://localhost:8080/login/oauth2/code/github
  • OAuth2LoginAuthenticationFilter.java
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
    MultiValueMap<String, String> params = OAuth2AuthorizationResponseUtils.toMultiMap(request.getParameterMap());
    if (!OAuth2AuthorizationResponseUtils.isAuthorizationResponse(params)) {
        OAuth2Error oauth2Error = new OAuth2Error("invalid_request");
        throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
    } else {
        OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestRepository.removeAuthorizationRequest(request, response);
        if (authorizationRequest == null) {
            OAuth2Error oauth2Error = new OAuth2Error("authorization_request_not_found");
            throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
        } else {
            String registrationId = (String)authorizationRequest.getAttribute("registration_id");
            ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);
            if (clientRegistration == null) {
                OAuth2Error oauth2Error = new OAuth2Error("client_registration_not_found", "Client Registration not found with Id: " + registrationId, (String)null);
                throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
            } else {
                String redirectUri = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request)).replaceQuery((String)null).build().toUriString();
                OAuth2AuthorizationResponse authorizationResponse = OAuth2AuthorizationResponseUtils.convert(params, redirectUri);
                Object authenticationDetails = this.authenticationDetailsSource.buildDetails(request);
                OAuth2LoginAuthenticationToken authenticationRequest = new OAuth2LoginAuthenticationToken(clientRegistration, new OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse));
                authenticationRequest.setDetails(authenticationDetails);
                // 委托Provider认证,认证成功后,返回OAuth2LoginAuthenticationToken
                OAuth2LoginAuthenticationToken authenticationResult = (OAuth2LoginAuthenticationToken)this.getAuthenticationManager().authenticate(authenticationRequest);
                OAuth2AuthenticationToken oauth2Authentication = new OAuth2AuthenticationToken(authenticationResult.getPrincipal(), authenticationResult.getAuthorities(), authenticationResult.getClientRegistration().getRegistrationId());
                oauth2Authentication.setDetails(authenticationDetails);
                OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(authenticationResult.getClientRegistration(), oauth2Authentication.getName(), authenticationResult.getAccessToken(), authenticationResult.getRefreshToken());
                this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, oauth2Authentication, request, response);
                return oauth2Authentication;
            }
        }
    }
}

OAuth2LoginAuthenticationFilter 的作用很简单,就是响应授权服务器的回调地址(/login/oauth2/code/github),核心之处在于OAuth2LoginAuthenticationProviderOAuth2LoginAuthenticationToken 的认证。

OAuth2LoginAuthenticationProvider.java

public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    OAuth2LoginAuthenticationToken loginAuthenticationToken = (OAuth2LoginAuthenticationToken)authentication;
    if (loginAuthenticationToken.getAuthorizationExchange().getAuthorizationRequest().getScopes().contains("openid")) {
        return null;
    } else {
        OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthenticationToken;
        try {
            authorizationCodeAuthenticationToken = (OAuth2AuthorizationCodeAuthenticationToken)this.authorizationCodeAuthenticationProvider.authenticate(new OAuth2AuthorizationCodeAuthenticationToken(loginAuthenticationToken.getClientRegistration(), loginAuthenticationToken.getAuthorizationExchange()));
        } catch (OAuth2AuthorizationException var9) {
            OAuth2Error oauth2Error = var9.getError();
            throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
        }

        OAuth2AccessToken accessToken = authorizationCodeAuthenticationToken.getAccessToken();
        Map<String, Object> additionalParameters = authorizationCodeAuthenticationToken.getAdditionalParameters();
        OAuth2User oauth2User = this.userService.loadUser(new OAuth2UserRequest(loginAuthenticationToken.getClientRegistration(), accessToken, additionalParameters));
        Collection<? extends GrantedAuthority> mappedAuthorities = this.authoritiesMapper.mapAuthorities(oauth2User.getAuthorities());
        OAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken(loginAuthenticationToken.getClientRegistration(), loginAuthenticationToken.getAuthorizationExchange(), oauth2User, mappedAuthorities, accessToken, authorizationCodeAuthenticationToken.getRefreshToken());
        authenticationResult.setDetails(loginAuthenticationToken.getDetails());
        return authenticationResult;
    }
}

OAuth2LoginAuthenticationToken.java

public OAuth2LoginAuthenticationToken(ClientRegistration clientRegistration, OAuth2AuthorizationExchange authorizationExchange, OAuth2User principal, Collection<? extends GrantedAuthority> authorities, OAuth2AccessToken accessToken, @Nullable OAuth2RefreshToken refreshToken) {
    super(authorities);
    Assert.notNull(clientRegistration, "clientRegistration cannot be null");
    Assert.notNull(authorizationExchange, "authorizationExchange cannot be null");
    Assert.notNull(principal, "principal cannot be null");
    Assert.notNull(accessToken, "accessToken cannot be null");
    this.clientRegistration = clientRegistration;
    this.authorizationExchange = authorizationExchange;
    this.principal = principal;
    this.accessToken = accessToken;
    this.refreshToken = refreshToken;
    this.setAuthenticated(true);
}

OAuth2LoginAuthenticationToken 有principal accessToken refreshToken等信息

参考阅读:

https://www.zyc.red/Spring/Security/OAuth2/OAuth2-Client/
Github:https://github.com/jkxyx205/spring-security-learn/tree/master/spring-security-oauth2-client