Thingsboard源码分析(二)OAuth2--SpringSecurity

  • 首先来说明一下 OAuth2 授权码模式 快捷登录流程:

    • 开发者先去Github申请一个应用,然后认证平台会给我们一个客户端Id(clientId)和密钥(clientSecret),还需要预留一个回调地址(http://localhost:8080/login/oauth2/code/)。
    • 当tb使用Github登录时,由thingsboard向Github发起验证请求,跳转到认证平台的用户授权页面(https://github.com/login/oauth/authorize)。
    • 如果用户确认授权,会得到一个code,然后认证平台会根据我们预留的回调地址重定向到thingsboard。然后thingsboard继续带着code去访问获取token的URI(https://github.com/login/oauth/access_token),认证平台确认无误后会发放token,从而完成认定。注意这个code只能用一次,用过即会过期。
    • 这样tb要获取用户信息(https://api.github.com/user)的时候就可以带着这个token去访问相应的接口。然后根据得到的用户信息创建或者查找User。
  • 安全起见,我已经将部分图片和解释进行处理,localhost:8080代表您部署thingsboard的主机IP和端口号

  • 下面开始分析源码,我之前已经使用Github账号登录过。在登录页面打开开发者工具(F12),选择网络准备进行抓包

  • 点击Github登录按钮,进行抓包,可以看到第一个请求地址是:http://localhost:8080/oauth2/authorization/83720530-0654-11ec-bfe6-372e3588ed6c ,这是为了获取并跳转到Github授权网页

  • 此时我们分析源码:ThingsboardSecurityConfiguration

     @Override
        protected void configure(HttpSecurity http) throws Exception {
            
           ...........................
               
            if (oauth2Configuration != null) {
                /* oauth2Configuration 已经注入到IOC容器中,在
                dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Configuration.java 
                后面会分析 */
                http.oauth2Login()
                        .authorizationEndpoint()
                        .authorizationRequestRepository(httpCookieOAuth2AuthorizationRequestRepository)
                        .authorizationRequestResolver(oAuth2AuthorizationRequestResolver)
                    // oAuth2AuthorizationRequestResolver 也已经注入到IOC容器,默认的是
                    // CustomOAuth2AuthorizationRequestResolver 后面会分析
                        .and()
                        .loginPage("/oauth2Login")
                    // 自定义登录界面
                        .loginProcessingUrl(oauth2Configuration.getLoginProcessingUrl())
                    // oauth2Configuration.getLoginProcessingUrl() 就是/login/oauth2/code/
                        .successHandler(oauth2AuthenticationSuccessHandler)
                    // 授权成功处理器
                        .failureHandler(oauth2AuthenticationFailureHandler);
                	// 授权失败处理器
            }
        }
    
  • 下面分析OAuth2Configuration

    @Configuration
    // 扫描时会注入到IOC容器
    @ConfigurationProperties(prefix = "security.oauth2")
    //从resources/thingsboard.yml中读取配置
    @Data
    public class OAuth2Configuration {
        private String loginProcessingUrl;
        // 即/login/oauth2/code/
        private Map<String, String> githubMapper;
    }
    
    # 见application/resources/thingsboard.yml 第116行
    oauth2:
        # Redirect URL where access code from external user management system will be processed
        loginProcessingUrl: "${SECURITY_OAUTH2_LOGIN_PROCESSING_URL:/login/oauth2/code/}"
        githubMapper:
          emailUrl: "${SECURITY_OAUTH2_GITHUB_MAPPER_EMAIL_URL_KEY:https://api.github.com/user/emails}"
    
    
  • OAuth2AuthorizationRequestResolver 是一个接口,Spring会将其实现类CustomOAuth2AuthorizationRequestResolver注入到容器中,并自动装载

    • 源码:

      @Service
      // 注入IOC容器
      @Slf4j
      public class CustomOAuth2AuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver {
          private static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization";
      .......
          private final AntPathRequestMatcher authorizationRequestMatcher = new AntPathRequestMatcher(
                  DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + "/{" + REGISTRATION_ID_URI_VARIABLE_NAME + "}");
          // 拦截 /oauth2/authorization 路径的请求
      .......
      }
      
    • 接下来是resolve方法,在讲resolve方法前先来解释一下ClientRegistration类:

      public final class ClientRegistration implements Serializable {
          private static final long serialVersionUID = 540L;
          private String registrationId;
          private String clientId;
          private String clientSecret;
          private ClientAuthenticationMethod clientAuthenticationMethod;
          private AuthorizationGrantType authorizationGrantType;
          private String redirectUri;
          private Set<String> scopes;
          private ClientRegistration.ProviderDetails providerDetails;
          private String clientName;
          
          ..........
              
          public static final class Builder implements Serializable {
              private static final long serialVersionUID = 540L;
              private String registrationId;
              private String clientId;
              private String clientSecret;
              private ClientAuthenticationMethod clientAuthenticationMethod;
              private AuthorizationGrantType authorizationGrantType;
              private String redirectUri;
              private Set<String> scopes;
              private String authorizationUri;
              private String tokenUri;
              private String userInfoUri;
              private AuthenticationMethod userInfoAuthenticationMethod;
              private String userNameAttributeName;
              private String jwkSetUri;
              private String issuerUri;
              private Map<String, Object> configurationMetadata;
              private String clientName;
          	...........
          }
          
          public class ProviderDetails implements Serializable {
              private static final long serialVersionUID = 540L;
              private String authorizationUri;
              private String tokenUri;
              private ClientRegistration.ProviderDetails.UserInfoEndpoint userInfoEndpoint = 
                  new ClientRegistration.ProviderDetails.UserInfoEndpoint();
              private String jwkSetUri;
              private String issuerUri;
              private Map<String, Object> configurationMetadata = Collections.emptyMap();
              ...........
          }    
          ...........
      }
            
      /** 有两个内部类,Builder是用来构建ClientRegistration对象的。主要是一些链式编程的方法来设置各个参数,最后build方法来构成一个ClientRegistration。ProviderDetails类主要是用来获取各种需要的参数。比如我们要获取Github授权地址(Authorization_url)应当使用clientRegistration.getProviderDetails().getAuthorizationUri()方法;要获取TokenUri(code换取access_token的地址)应该是clientRegistration.getProviderDetails().getTokenUri()**/
      
    • 下面来介绍CustomOAuth2AuthorizationRequestResolver的resolve方法。前面AntPathRequestMatcher拦截到/oauth2/authorization路径的登录请求,下面来重定向到获取授权地址以获取code

      @Override
      public OAuth2AuthorizationRequest resolve(HttpServletRequest request) {
              String registrationId = this.resolveRegistrationId(request);
          /*  我们捕获的第一个请求:
          http://localhost:8080/oauth2/authorization/83720530-0654-
          11ec-bfe6-372e3588ed6c 。
          这里registrationId = 83720530-0654-11ec-bfe6-372e3588ed6c
          */
              String redirectUriAction = getAction(request, "login");
          // redirectUriAction = "login"
              String appPackage = getAppPackage(request);
              String appToken = getAppToken(request);
          // appPackage = appToken = null;
              return resolve(request, registrationId, redirectUriAction, appPackage, appToken);
          }
          
      
      //该方法用于向指定Authorization_URL发起一次OAuth2请求
      @SuppressWarnings("deprecation")
      private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction, String appPackage, String appToken) {
              if (registrationId == null) {
                  return null;
              }
      
              ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);
          // ClientRegistration 是用于存放OAuth记录的实体类,包含了一些client_id,client_secret等相关的参数,后面解释
          
              if (clientRegistration == null) {
                  throw new IllegalArgumentException("Invalid Client Registration with Id: " + registrationId);
              }
      
              Map<String, Object> attributes = new HashMap<>();
              attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId());
          // OAuth2ParameterNames.REGISTRATION_ID = "registration_id"
          
          //appPackage = null ,下面的if不会执行
          /**    if (!StringUtils.isEmpty(appPackage)) {
                  if (StringUtils.isEmpty(appToken)) {
                      throw new IllegalArgumentException("Invalid application token.");
                  } else {
                      String appSecret = this.oAuth2Service.findAppSecret(UUID.fromString(registrationId), appPackage);
                      if (StringUtils.isEmpty(appSecret)) {
                          throw new IllegalArgumentException("Invalid package: " + appPackage + ". No application secret found for Client Registration with given application package.");
                      }
                      String callbackUrlScheme = this.oAuth2AppTokenFactory.validateTokenAndGetCallbackUrlScheme(appPackage, appToken, appSecret);
                      attributes.put(TbOAuth2ParameterNames.CALLBACK_URL_SCHEME, callbackUrlScheme);
                  }
              }
      	**/
              OAuth2AuthorizationRequest.Builder builder;
              if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) {
                  // AuthorizationGrantType.AUTHORIZATION_CODE = new AuthorizationGrantType("authorization_code");
                  builder = OAuth2AuthorizationRequest.authorizationCode();
                  // 下面添加一些附加信息
                  Map<String, Object> additionalParameters = new HashMap<>();
                  if (!CollectionUtils.isEmpty(clientRegistration.getScopes()) &&
                          clientRegistration.getScopes().contains(OidcScopes.OPENID)) {
                      // Section 3.1.2.1 Authentication Request - https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
                      // scope
                      // 		REQUIRED. OpenID Connect requests MUST contain the "openid" scope value.
                      addNonceParameters(attributes, additionalParameters);
                  }
                  if (ClientAuthenticationMethod.NONE.equals(clientRegistration.getClientAuthenticationMethod())) {
                      addPkceParameters(attributes, additionalParameters);
                  }
                  builder.additionalParameters(additionalParameters);
              } else if (AuthorizationGrantType.IMPLICIT.equals(clientRegistration.getAuthorizationGrantType())) {
                  builder = OAuth2AuthorizationRequest.implicit();
              } else {
                  throw new IllegalArgumentException("Invalid Authorization Grant Type ("  +
                          clientRegistration.getAuthorizationGrantType().getValue() +
                          ") for Client Registration with Id: " + clientRegistration.getRegistrationId());
              }
      
              String redirectUriStr = expandRedirectUri(request, clientRegistration, redirectUriAction);
      
              return builder
                      .clientId(clientRegistration.getClientId())
      					// OAth2 认证时在Github申请的client_id
                      .authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri())
                  		// Github授权链接:https://github.com/login/oauth/authorize
                      .redirectUri(redirectUriStr)
                      	// Github授权之后重定向地址:http://localhost:8080/login/oauth2/code/
                      .scopes(clientRegistration.getScopes())
                  		// 获取信息范围,cope=read:user%20user:email
                      .state(this.stateGenerator.generateKey())
                  		// state,这里没有具体含义。第Github认证通过之后还会原封不动返回
                      .attributes(attributes)
                      .build();
          
          // 因此,thingsboard会发送一次OAuth2请求,同时也是我们抓包抓到的第二个请求:
          //https://github.com/login/oauth/authorize?response_type=code&client_id=c1b40b3634e31c62d4fc&scope=read:user%20user:email&state=0orHfMSwdDQl2IB-PCPckAWxFhUgFa8-UuddUsPMueQ%3D&redirect_uri=http://localhost:8080/login/oauth2/code/
          }
      
  • 当Github完成授权,会将code按照redirect_URL返回给thingsboard:http://localhost:8080/login/oauth2/code/?code=80ef8e5fdc54e35a1cce&state=0orHfMSwdDQl2IB-PCPckAWxFhUgFa8-UuddUsPMueQ= 这正是我们抓包第三次请求。然后我们在请求头中发现了返回的token(注意,为了安全考虑,由Github返回的token不可能返回给前端,这由thingsboard为当前登录的User创建的accessToken 和 refreshToken,而不是Github的accessToken):
    在这里插入图片描述

    可是这个accessToken 和 refreshToken 是哪里来的呢?继续分析源码

  • 当Github授权完成后向thingsboard后端/login/oauth2/code接口发送code。后面具体操作有待研究,我的猜想是:后端拦截到这个路径的请求,再调用clientRegistration.getProviderDetails().getTokenUri()方法获取到tokenUri ,然后来根据code换取accessToken 。在源码中可以看到以下相关代码:

    • public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> extends AbstractAuthenticationFilterConfigurer<B, OAuth2LoginConfigurer<B>, OAuth2LoginAuthenticationFilter> {
          private final OAuth2LoginConfigurer<B>.AuthorizationEndpointConfig authorizationEndpointConfig 
              = new OAuth2LoginConfigurer.AuthorizationEndpointConfig();
          private final OAuth2LoginConfigurer<B>.TokenEndpointConfig tokenEndpointConfig 
              = new OAuth2LoginConfigurer.TokenEndpointConfig();
          private final OAuth2LoginConfigurer<B>.RedirectionEndpointConfig redirectionEndpointConfig 
              = new OAuth2LoginConfigurer.RedirectionEndpointConfig();
          private final OAuth2LoginConfigurer<B>.UserInfoEndpointConfig userInfoEndpointConfig 
              = new OAuth2LoginConfigurer.UserInfoEndpointConfig();
          private String loginPage;
          private String loginProcessingUrl = "/login/oauth2/code/*";
      
          // 修改回调地址
          public OAuth2LoginConfigurer<B> loginProcessingUrl(String loginProcessingUrl) {
              Assert.hasText(loginProcessingUrl, "loginProcessingUrl cannot be empty");
              this.loginProcessingUrl = loginProcessingUrl;
              return this;
          }
          .........
          public void init(B http) throws Exception {
              // 由构造器第三个参数可以见得,这是在添加一个对 /login/oauth2/code/ 路径的拦截器
              OAuth2LoginAuthenticationFilter authenticationFilter 
                  = new OAuth2LoginAuthenticationFilter(
       OAuth2ClientConfigurerUtils.getClientRegistrationRepository((HttpSecurityBuilder)this.getBuilder()), OAuth2ClientConfigurerUtils.getAuthorizedClientRepository((HttpSecurityBuilder)this.getBuilder()), this.loginProcessingUrl);
              this.setAuthenticationFilter(authenticationFilter);
              super.loginProcessingUrl(this.loginProcessingUrl);
              if (this.loginPage != null) {
                  super.loginPage(this.loginPage);
                  // loginPage = "/oauth2Login"
                  super.init(http);
              } else {
                  Map<String, String> loginUrlToClientName = this.getLoginLinks();
                  if (loginUrlToClientName.size() == 1) {
                      this.updateAuthenticationDefaults();
                      this.updateAccessDefaults(http);
                      String providerLoginPage = (String)loginUrlToClientName.keySet().iterator().next();
                      this.registerAuthenticationEntryPoint(http, this.getLoginEntryPoint(http, providerLoginPage));
                  } else {
                      super.init(http);
                  }
              }
      
              OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient = this.tokenEndpointConfig.accessTokenResponseClient;
              if (accessTokenResponseClient == null) {
                  accessTokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient();
              }
      
              OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService = this.getOAuth2UserService();
              OAuth2LoginAuthenticationProvider oauth2LoginAuthenticationProvider = new OAuth2LoginAuthenticationProvider((OAuth2AccessTokenResponseClient)accessTokenResponseClient, oauth2UserService);
              GrantedAuthoritiesMapper userAuthoritiesMapper = this.getGrantedAuthoritiesMapper();
              if (userAuthoritiesMapper != null) {
                  oauth2LoginAuthenticationProvider.setAuthoritiesMapper(userAuthoritiesMapper);
              }
      
              http.authenticationProvider((AuthenticationProvider)this.postProcess(oauth2LoginAuthenticationProvider));
              boolean oidcAuthenticationProviderEnabled = ClassUtils.isPresent("org.springframework.security.oauth2.jwt.JwtDecoder", this.getClass().getClassLoader());
              if (oidcAuthenticationProviderEnabled) {
                  OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService = this.getOidcUserService();
                  OidcAuthorizationCodeAuthenticationProvider oidcAuthorizationCodeAuthenticationProvider = new OidcAuthorizationCodeAuthenticationProvider((OAuth2AccessTokenResponseClient)accessTokenResponseClient, oidcUserService);
                  JwtDecoderFactory<ClientRegistration> jwtDecoderFactory = this.getJwtDecoderFactoryBean();
                  if (jwtDecoderFactory != null) {
                      oidcAuthorizationCodeAuthenticationProvider.setJwtDecoderFactory(jwtDecoderFactory);
                  }
      
                  if (userAuthoritiesMapper != null) {
                      oidcAuthorizationCodeAuthenticationProvider.setAuthoritiesMapper(userAuthoritiesMapper);
                  }
      
                  http.authenticationProvider((AuthenticationProvider)this.postProcess(oidcAuthorizationCodeAuthenticationProvider));
              } else {
                  http.authenticationProvider(new OAuth2LoginConfigurer.OidcAuthenticationRequestChecker());
              }
      
              this.initDefaultLoginFilter(http);
          }
          .........
          // 创建匹配器
          protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
              return new AntPathRequestMatcher(loginProcessingUrl);
          }
          ........
      }
      
  • 授权成功,应当交给Oauth2AuthenticationSuccessHandler处理:

    package org.thingsboard.server.service.security.auth.oauth2;
    @Slf4j
    @Component(value = "oauth2AuthenticationSuccessHandler")
    public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
        // 如果授权成功
    	@Override
        public void onAuthenticationSuccess(HttpServletRequest request,
                                            HttpServletResponse response,
                                            Authentication authentication) throws IOException {
            OAuth2AuthorizationRequest authorizationRequest = httpCookieOAuth2AuthorizationRequestRepository.loadAuthorizationRequest(request);
            
        /**  httpCookieOAuth2AuthorizationRequestRepository主要时用来持久化OAuth2请求,部分代码如下:
        public static final String OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME = "oauth2_auth_request";
        
        @Override
        public OAuth2AuthorizationRequest loadAuthorizationRequest(HttpServletRequest request) {
            return CookieUtils.getCookie(request, OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME)
                    .map(cookie -> CookieUtils.deserialize(cookie, OAuth2AuthorizationRequest.class))
                    .orElse(null);
        }
        **/
            
            // String CALLBACK_URL_SCHEME = "callback_url_scheme";
            String callbackUrlScheme = authorizationRequest.getAttribute(TbOAuth2ParameterNames.CALLBACK_URL_SCHEME);
            // callbackUrlScheme = null
            String baseUrl;
            if (!StringUtils.isEmpty(callbackUrlScheme)) {
                baseUrl = callbackUrlScheme + ":";
            } else {
                baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request);
            }
            // baseUrl = http://localhost:8080
            try {
                OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication;
    
                OAuth2Registration registration = oAuth2Service.findRegistration(UUID.fromString(token.getAuthorizedClientRegistrationId()));
                
                OAuth2AuthorizedClient oAuth2AuthorizedClient = oAuth2AuthorizedClientService.loadAuthorizedClient(
                        token.getAuthorizedClientRegistrationId(),
                        token.getPrincipal().getName());
                
                OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(registration.getMapperConfig().getType());
                // mapper = GithubOAuth2ClientMapper.java , 后面会解释
                
                // 根据Github提供的AcessToken来获取Email,进而获取或者新建一个User
                SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(request, token, oAuth2AuthorizedClient.getAccessToken().getTokenValue(),
                        registration);
    			
                // 为当前用户产生 accessToken和refreshToken
                JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser);
                JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser);
    
                clearAuthenticationAttributes(request, response);
                getRedirectStrategy().sendRedirect(request, response, baseUrl + "/?accessToken=" + accessToken.getToken() + "&refreshToken=" + refreshToken.getToken());
            } catch (Exception e) {
                log.debug("Error occurred during processing authentication success result. " +
                        "request [{}], response [{}], authentication [{}]", request, response, authentication, e);
                clearAuthenticationAttributes(request, response);
                String errorPrefix;
                if (!StringUtils.isEmpty(callbackUrlScheme)) {
                    errorPrefix = "/?error=";
                } else {
                    errorPrefix = "/login?loginError=";
                }
                getRedirectStrategy().sendRedirect(request, response, baseUrl + errorPrefix +
                        URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8.toString()));
            }
        }
    }
    
  • 上面有提到GithubOAuth2ClientMapper,它是OAuth2ClientMapper的一个实现类

    @Service(value = "githubOAuth2ClientMapper")
    @Slf4j
    public class GithubOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper {
        private static final String EMAIL_URL_KEY = "emailUrl";
    
        private static final String AUTHORIZATION = "Authorization";
    	 
        private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
    
        @Override
        public SecurityUser getOrCreateUserByClientPrincipal(HttpServletRequest request, OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) {
            OAuth2MapperConfig config = registration.getMapperConfig();
            Map<String, String> githubMapperConfig = oAuth2Configuration.getGithubMapper();
            /**
           		githubMapper:     				  emailUrl:"${SECURITY_OAUTH2_GITHUB_MAPPER_EMAIL_URL_KEY:https://api.github.com/user/emails}"
            **/
            
            // 根据Github提供的AccessToken来获取Email
            String email = getEmail(githubMapperConfig.get(EMAIL_URL_KEY), providerAccessToken);
            Map<String, Object> attributes = token.getPrincipal().getAttributes();
            OAuth2User oAuth2User = BasicMapperUtils.getOAuth2User(email, attributes, config);
            
            // 这是父类 AbstractOAuth2ClientMapper 的一个方法
            return getOrCreateSecurityUserFromOAuth2User(oAuth2User, registration);
        }
    }
    
  • GithubOAuth2ClientMapper 父类 AbstractOAuth2ClientMapper

    @Slf4j
    public abstract class AbstractOAuth2ClientMapper {
        @Autowired
        private UserService userService;
        
        protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, OAuth2Registration registration) {
    
            OAuth2MapperConfig config = registration.getMapperConfig();
    
            UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, oauth2User.getEmail());
    
            // 根据Email查找User
            User user = userService.findUserByEmail(TenantId.SYS_TENANT_ID, oauth2User.getEmail());
    		
            if (user == null && !config.isAllowUserCreation()) {
                throw new UsernameNotFoundException("User not found: " + oauth2User.getEmail());
            }
    
            // 查无对象则创建对象
            if (user == null) {
                userCreationLock.lock();
                try {                
                    user = userService.findUserByEmail(TenantId.SYS_TENANT_ID, oauth2User.getEmail());
                     // 查无对象则创建对象,然后设置一系列参数
                    if (user == null) {
                        user = new User();
                        // 若customerId =null 且 customerName = null 则创建tenant,否则创建customer。这就是为什么Github登录创建的是Tenant
                        if (oauth2User.getCustomerId() == null && StringUtils.isEmpty(oauth2User.getCustomerName())) {
                            user.setAuthority(Authority.TENANT_ADMIN);
                        } else {
                            user.setAuthority(Authority.CUSTOMER_USER);
                        }
                        
                        TenantId tenantId = oauth2User.getTenantId() != null ?
                                oauth2User.getTenantId() : getTenantId(oauth2User.getTenantName());
                        user.setTenantId(tenantId);
                        CustomerId customerId = oauth2User.getCustomerId() != null ?
                                oauth2User.getCustomerId() : getCustomerId(user.getTenantId(), oauth2User.getCustomerName());
                        user.setCustomerId(customerId);
                        user.setEmail(oauth2User.getEmail());
                        user.setFirstName(oauth2User.getFirstName());
                        user.setLastName(oauth2User.getLastName());
    
                        ObjectNode additionalInfo = objectMapper.createObjectNode();
    
                        if (!StringUtils.isEmpty(oauth2User.getDefaultDashboardName())) {
                            Optional<DashboardId> dashboardIdOpt =
                                    user.getAuthority() == Authority.TENANT_ADMIN ?
                                            getDashboardId(tenantId, oauth2User.getDefaultDashboardName())
                                            : getDashboardId(tenantId, customerId, oauth2User.getDefaultDashboardName());
                            if (dashboardIdOpt.isPresent()) {
                                additionalInfo.put("defaultDashboardFullscreen", oauth2User.isAlwaysFullScreen());
                                additionalInfo.put("defaultDashboardId", dashboardIdOpt.get().getId().toString());
                            }
                        }
    
                        if (registration.getAdditionalInfo() != null &&
                                registration.getAdditionalInfo().has("providerName")) {
                            additionalInfo.put("authProviderName", registration.getAdditionalInfo().get("providerName").asText());
                        }
    
                        user.setAdditionalInfo(additionalInfo);
    					
                        // 将创建的用户保存起来
                        user = userService.saveUser(user);
                        // 设置密码为空
                        if (config.isActivateUser()) {
                            UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId());
                            userService.activateUserCredentials(user.getTenantId(), userCredentials.getActivateToken(), passwordEncoder.encode(""));
                        }
                    }
                } catch (Exception e) {
                    log.error("Can't get or create security user from oauth2 user", e);
                    throw new RuntimeException("Can't get or create security user from oauth2 user", e);
                } finally {
                    userCreationLock.unlock();
                }
            }
    
            try {
                SecurityUser securityUser = new SecurityUser(user, true, principal);
                return (SecurityUser) new UsernamePasswordAuthenticationToken(securityUser, null, securityUser.getAuthorities()).getPrincipal();
            } catch (Exception e) {
                log.error("Can't get or create security user from oauth2 user", e);
                throw new RuntimeException("Can't get or create security user from oauth2 user", e);
            }
        }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值