自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

用過的20個(gè)高顏值登錄頁,個(gè)個(gè)都創(chuàng)意十足!

開發(fā) 項(xiàng)目管理
本文詳細(xì)介紹了如何在項(xiàng)目中實(shí)現(xiàn)自定義的第三方用戶關(guān)聯(lián)邏輯,通過繼承 DefaultOAuth2UserService 類來處理用戶登錄和數(shù)據(jù)綁定。我們首先分析了三方登錄的認(rèn)證流程,并創(chuàng)建了必要的數(shù)據(jù)庫結(jié)構(gòu)以存儲(chǔ)第三方用戶信息。

在項(xiàng)目中集成第三方登錄后,我們需要將第三方平臺(tái)的賬號(hào)與我們自己的賬號(hào)體系關(guān)聯(lián)。例如,當(dāng)用戶選擇使用微信登錄時(shí),還需綁定一個(gè)手機(jī)號(hào)。這個(gè)手機(jī)號(hào)的綁定操作實(shí)際上是將微信賬號(hào)與我們系統(tǒng)中的賬號(hào)進(jìn)行關(guān)聯(lián)。本文將詳細(xì)介紹如何在選擇使用Gitee進(jìn)行登錄時(shí),將其與系統(tǒng)用戶表 sys_user 進(jìn)行綁定。

1. SAS三方平臺(tái)認(rèn)證邏輯

如前所述,在SAS中,當(dāng)?shù)谌秸J(rèn)證成功后,會(huì)回調(diào)配置的接口 /login/oauth2/code/*。該接口會(huì)被過濾器 OAuth2LoginAuthenticationFilter 攔截并處理。在執(zhí)行核心邏輯 authenticate() 方法時(shí),會(huì)交由 OAuth2LoginAuthenticationProvider 進(jìn)行處理。

OAuth2LoginAuthenticationToken authenticationResult = (OAuth2LoginAuthenticationToken) this  
    .getAuthenticationManager()  
    .authenticate(authenticationRequest);

在 OAuth2LoginAuthenticationProvider#authenticate 方法中,通過 OAuth2UserService 加載用戶信息:

@Override  
public Authentication authenticate(Authentication authentication) throws AuthenticationException {  
    OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthenticationToken;  
 ... 
    OAuth2User oauth2User = this.userService.loadUser(new OAuth2UserRequest(  
          loginAuthenticationToken.getClientRegistration(), accessToken, additionalParameters));  
 ...
    return authenticationResult;  
}

loadUser 方法由 DefaultOAuth2UserService 負(fù)責(zé)實(shí)現(xiàn),通過 RestTemplate 調(diào)用 Gitee 平臺(tái)獲取用戶信息。

圖片圖片

public class DefaultOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {  
  
    @Override  
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {  
       ...
       //構(gòu)建請(qǐng)求
       RequestEntity<?> request = this.requestEntityConverter.convert(userRequest);  
       ResponseEntity<Map<String, Object>> response = getResponse(userRequest, request);  
       Map<String, Object> userAttributes = response.getBody();  
       Set<GrantedAuthority> authorities = new LinkedHashSet<>();  
       authorities.add(new OAuth2UserAuthority(userAttributes));  
       OAuth2AccessToken token = userRequest.getAccessToken();  
       for (String authority : token.getScopes()) {  
          authorities.add(new SimpleGrantedAuthority("SCOPE_" + authority));  
       }  
       return new DefaultOAuth2User(authorities, userAttributes, userNameAttributeName);  
    }
    
    //獲取用戶響應(yīng)
    private ResponseEntity<Map<String, Object>> getResponse(OAuth2UserRequest userRequest, RequestEntity<?> request) {
  try {
   return this.restOperations.exchange(request, PARAMETERIZED_RESPONSE_TYPE);
  }
  ...
 }
    ...
}

2. 實(shí)現(xiàn)自定義用戶關(guān)聯(lián)邏輯

通過對(duì)三方登錄流程的分析,我們可以通過繼承 DefaultOAuth2UserService 類來實(shí)現(xiàn)自定義的用戶關(guān)聯(lián)邏輯。

public class CustomOAuth2UserService extends DefaultOAuth2UserService {
    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        OAuth2User oauth2User = super.loadUser(userRequest);
        // 在這里實(shí)現(xiàn)用戶綁定邏輯,例如與 sys_user 表進(jìn)行關(guān)聯(lián)
        ...
        return oauth2User;
    }
}

接下來思考一下,將第三方的賬戶轉(zhuǎn)換成我們的自定義用戶需要做哪些事?

1、首先,通過 super.loadUser 方法獲取到第三方用戶對(duì)象 OAuth2User。

2、由于數(shù)據(jù)結(jié)構(gòu)存在差異,我們還需將 OAuth2User 轉(zhuǎn)換為我們自己的用戶數(shù)據(jù)結(jié)構(gòu)。

3、數(shù)據(jù)轉(zhuǎn)換后,需要驗(yàn)證第三方賬號(hào)是否在系統(tǒng)中存在。如果不存在,則進(jìn)行保存操作并關(guān)聯(lián)賬號(hào);如果存在,則執(zhí)行更新操作。

以下為實(shí)現(xiàn)過程。

2.1 存儲(chǔ)第三方用戶

首先,創(chuàng)建一張表用于存儲(chǔ)第三方用戶,其建表語句如下:

CREATE TABLE `oauth2_third_user`  (
                                      `id` int NOT NULL AUTO_INCREMENT,
                                      `user_id` int NULL DEFAULT NULL COMMENT '用戶ID',
                                      `unique_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '第三方用戶ID',
                                      `unique_account` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
                                      `unique_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '第三方用戶賬號(hào)',
                                      `platform` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '平臺(tái)類型',
                                      `credentials` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT 'token信息',
                                      `credentials_expires_at` datetime NULL DEFAULT NULL,
                                      `create_time` datetime NULL DEFAULT NULL COMMENT '綁定時(shí)間',
                                      `update_time` datetime NULL DEFAULT NULL COMMENT '更新時(shí)間',
                                      PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

在這個(gè)表中,通過字段 user_id 與我們自己的用戶表 sys_user 進(jìn)行關(guān)聯(lián),同時(shí)通過第三方登錄平臺(tái) platform 與第三方用戶 ID unique_id 來確定唯一用戶。

2.2 創(chuàng)建接口用于將第三方用戶轉(zhuǎn)化成我們自己的用戶

public interface OAuth2UserConvert {
    /**
     * 轉(zhuǎn)換成自定義用戶
     * @param oAuth2User Oauth2用戶
     * @return Oauth2UnionUser
     */
    Oauth2UnionUser convert(OAuth2User oAuth2User );
}

由于本文集成的是 Gitee 平臺(tái),因此需要編寫一個(gè)具體的實(shí)現(xiàn)類用于用戶轉(zhuǎn)換:

public class GiteeUserConvert implements OAuth2UserConvert{
    
    private final static String AVATAR_URL = "avatar_url";
    private final static String UNIQUE_ID = "id";
    private final static String ACCOUNT = "login";
    private final static String NAME = "name";
    private final static String EMAIL = "email";


    @Override
    public Oauth2UnionUser convert(OAuth2User oAuth2User) {
        // 獲取三方用戶信息
        String avatarUrl = Optional.ofNullable(oAuth2User.getAttribute(AVATAR_URL)).map(Object::toString).orElse(null);
        String uniqueId = Optional.ofNullable(oAuth2User.getAttribute(UNIQUE_ID)).map(Object::toString).orElse(null);
        String uniqueAccount = Optional.ofNullable(oAuth2User.getAttribute(ACCOUNT)).map(Object::toString).orElse(null);
        String email = Optional.ofNullable(oAuth2User.getAttribute(EMAIL)).map(Object::toString).orElse(null);
        String nickName = Optional.ofNullable(oAuth2User.getAttribute(NAME)).map(Object::toString).orElse(null);

        // 轉(zhuǎn)換至Oauth2ThirdAccount
        Oauth2UnionUser unionUser = new Oauth2UnionUser();
        unionUser.setUniqueId(uniqueId);
        unionUser.setUniqueAccount(uniqueAccount);
        unionUser.setAvatarUrl(avatarUrl);
        unionUser.setNickName(nickName);
        unionUser.setEmail(email);
        unionUser.setPlatform(ThirdPlatFormEnum.GITEE.name());

        return unionUser;
    }
}

當(dāng)然,如果需要集成多個(gè)平臺(tái),還需要?jiǎng)?chuàng)建一個(gè)上下文類,用于選擇具體的接口實(shí)現(xiàn)進(jìn)行用戶轉(zhuǎn)換。

@Component
@RequiredArgsConstructor
public class Oauth2UserConverterContext {

    /**
     * 用戶轉(zhuǎn)換器
     */
    public Oauth2UnionUser convert(OAuth2UserRequest userRequest, OAuth2User oAuth2User) {
        String userNameAttributeName = userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName();
        // 獲取三方登錄配置的registrationId,這里將他當(dāng)做登錄方式
        String registrationId = userRequest.getClientRegistration().getRegistrationId();
        // 轉(zhuǎn)換用戶信息
        Oauth2UnionUser oauth2UnionUser = this.getInstance(registrationId).convert(oAuth2User);

        oauth2UnionUser.setUserNameAttributeName(userNameAttributeName);
        // 獲取AccessToken
        OAuth2AccessToken accessToken = userRequest.getAccessToken();
        oauth2UnionUser.setCredentials(accessToken.getTokenValue());

        Instant expiresAt = accessToken.getExpiresAt();
        if (expiresAt != null) {
            LocalDateTime tokenExpiresAt = expiresAt.atZone(ZoneId.of("UTC")).toLocalDateTime();
            // token過期時(shí)間
            oauth2UnionUser.setCredentialsExpiresAt(tokenExpiresAt);
        }

        return oauth2UnionUser;
    }

    /**
     * 獲取轉(zhuǎn)換器
     * @param registrationId 登錄類型
     * @return 轉(zhuǎn)換器
     */
    private OAuth2UserConvert getInstance(String registrationId) {
        if (Objects.isNull(registrationId)){
            throw new UnsupportedOperationException("登錄方式不能為空.");
        }

        return switch (registrationId) {
            case "github" -> new GithubUserConvert();
            case "gitee" -> new GiteeUserConvert();
            default -> throw new IllegalStateException("Unexpected value: " + registrationId);
        };
    }
}

在這段代碼中,通過第三方登錄平臺(tái)的 registrationId 來選擇具體的接口實(shí)現(xiàn)類。

2.3 創(chuàng)建Oauth2ThirdService用于實(shí)現(xiàn)用戶的存儲(chǔ)邏輯

@Service
@RequiredArgsConstructor
public class Oauth2ThirdServiceImpl implements Oauth2ThirdService {

    private final SysUserService sysUserService;

    private final Oauth2ThirdUserMapper oauth2ThirdUserMapper;

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void save(Oauth2UnionUser oauth2UnionUser) {
  //查詢用戶是否存在,通過平臺(tái)和第三方的ID兩個(gè)字段確定唯一用戶
        LambdaQueryWrapper<Oauth2ThirdUserDO> queryWrapper = Wrappers.lambdaQuery(Oauth2ThirdUserDO.class)
                .eq(Oauth2ThirdUserDO::getPlatform, oauth2UnionUser.getPlatform())
                .eq(Oauth2ThirdUserDO::getUniqueId, oauth2UnionUser.getUniqueId());

        Oauth2ThirdUserDO oauth2ThirdUserDO = oauth2ThirdUserMapper.selectOne(queryWrapper);

        //數(shù)據(jù)庫如果為空,則先保存到系統(tǒng)用戶表,然后再初始化到第三方用戶表
        if(oauth2ThirdUserDO == null){
            Integer userId = sysUserService.saveByThirdUser(oauth2UnionUser);

            Oauth2ThirdUserDO thirdUserDO = convertThirdUser(oauth2UnionUser);
            thirdUserDO.setUserId(userId);
            oauth2ThirdUserMapper.insert(thirdUserDO);
        }else {
            oauth2ThirdUserDO.setCredentialsExpiresAt(oauth2UnionUser.getCredentialsExpiresAt());
            oauth2ThirdUserDO.setCredentials(oauth2UnionUser.getCredentials());
            oauth2ThirdUserDO.setUpdateTime(LocalDateTime.now());
            oauth2ThirdUserMapper.updateById(oauth2ThirdUserDO);
        }
    }

 ...
}

2.4 繼承DefaultOAuth2UserService,用于業(yè)務(wù)流程編排

@Service
@RequiredArgsConstructor
@Slf4j
public class CustomOauth2UnionService extends DefaultOAuth2UserService {

    private final Oauth2UserConverterContext oauth2UserConverterContext;

    private final Oauth2ThirdService oauth2ThirdService;

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        //1、獲取到遠(yuǎn)程用戶信息
        OAuth2User oAuth2User = super.loadUser(userRequest);
        //2、轉(zhuǎn)換用戶信息
        Oauth2UnionUser oauth2UnionUser = oauth2UserConverterContext.convert(userRequest, oAuth2User);
        //3、檢查是否存在并保存
        oauth2ThirdService.save(oauth2UnionUser);

        // 將yml配置的RegistrationId當(dāng)做登錄類型設(shè)置至attributes中
        LinkedHashMap<String, Object> attributes = new LinkedHashMap<>(oAuth2User.getAttributes());
        attributes.put("platform", oauth2UnionUser.getPlatform());
        return new DefaultOAuth2User(oAuth2User.getAuthorities(), attributes, oauth2UnionUser.getUserNameAttributeName());
    }

}

通過上面四步處理,當(dāng)我們初次使用Gitee平臺(tái)登錄時(shí),會(huì)在sys_user中先插入一條數(shù)據(jù),然后再在oauth2_third_user表中插入第三方用戶數(shù)據(jù),這樣就實(shí)現(xiàn)了用戶數(shù)據(jù)的綁定。

圖片圖片

3. 小結(jié)

本文詳細(xì)介紹了如何在項(xiàng)目中實(shí)現(xiàn)自定義的第三方用戶關(guān)聯(lián)邏輯,通過繼承 DefaultOAuth2UserService 類來處理用戶登錄和數(shù)據(jù)綁定。我們首先分析了三方登錄的認(rèn)證流程,并創(chuàng)建了必要的數(shù)據(jù)庫結(jié)構(gòu)以存儲(chǔ)第三方用戶信息。

接著,我們定義了用戶轉(zhuǎn)換接口和具體的實(shí)現(xiàn)類,以便將第三方用戶信息轉(zhuǎn)換為我們自定義的用戶數(shù)據(jù)結(jié)構(gòu)。為了實(shí)現(xiàn)數(shù)據(jù)的有效存儲(chǔ),我們?cè)O(shè)計(jì)了一個(gè)服務(wù)類,用于檢查用戶是否已存在于系統(tǒng)中,并進(jìn)行相應(yīng)的保存或更新操作。

責(zé)任編輯:武曉燕 來源: JAVA日知錄
相關(guān)推薦

2022-01-26 18:59:08

Python工具

2022-01-11 09:05:07

工具Python 命令行

2011-10-09 16:20:08

MongoDBJournaling

2018-02-01 07:37:20

運(yùn)營商5G互聯(lián)網(wǎng)

2012-08-22 09:10:45

Chrome OS操作系統(tǒng)

2020-08-31 10:16:14

Windows 10微軟更新

2015-04-01 11:47:56

京東彈性云

2022-07-25 10:07:26

Python可視化技巧

2017-08-20 13:32:09

2013-05-02 14:02:58

App

2015-09-29 10:37:35

LG

2009-01-15 09:21:28

北電網(wǎng)絡(luò)破產(chǎn)保護(hù)虧損

2016-08-22 13:13:18

云計(jì)算云服務(wù)公有云

2017-07-27 10:36:20

銀河護(hù)衛(wèi)隊(duì) OptiPlex家族

2024-07-29 00:00:01

Input源碼

2013-11-20 09:39:56

Windows 8.1Windows 8.1

2021-08-04 05:32:40

Web動(dòng)畫CSS技巧

2012-02-01 17:06:35

2025-02-03 10:00:00

DeepSeekChatGPT人工智能
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)