Spring OAuth2 GitHub 自定義登錄信息


# 相關代碼

 

https://github.com/mofadeyunduo/money

0.1.3-SNAPSHOT

security 模塊中

 

# 原因

 

最近在做一款管理金錢的網站進行自娛自樂,發現沒有安全控制豈不是大家都知道我的工資了(一臉黑線)?

最近公司也在搞 Spring OAuth2,當時我沒有時間(其實那時候不想搞)就沒做,現在回頭來學習學習。

Spring OAuth2 官方的教程寫的比較少,實用性比較差。

 

# 教程內容

 

  1. Spring OAuth2 Github SSO
  2. 替換認證過的 Spring Security Authentication 對象
  3. 后端設置 Cookie 實現 Remember-Me 功能

 

# Github 登錄

 

Spring OAuth2 Github,官網寫的已經比較詳細了。

要注意的是必須要配置兩個 Filter:

  • OAuth2ClientAuthenticationProcessingFilter ,用於處理 OAuth2 流程。
  • OAuth2ClientContextFilter,用於觸發跳轉到 OAuth2 請求。

P.S. 這很詭異,我不知道 Spring OAuth2 為什么要這么做。

 

# 替換認證過的 Spring Security Authentication

 

步驟:

  1. 重寫方法 getPrincipal,把 Authentication 中的 Principal 換成用戶信息對象。
  2. 最好設置 AuthenticationSuccessHandler,默認的 AuthenticationSuccessHandler 會跳轉到主頁,並將 Cookie 清空,影響 RememberMeServices 認證。
    @Override
    protected Object getPrincipal(Map<String, Object> map) {
        String principal = String.class.cast(map.get("name"));
        // 從數據庫讀取用戶
        UserWithAccount userWithAccount = userAndAccountService.getByPrincipalAndType(principal, AccountType.GITHUB);
        // 未獲取到用戶信息,保存
        if (Objects.isNull(userWithAccount)) {
            Account newAccount = new Account(null, AccountType.GITHUB, principal, new Gson().toJson(map));
            userWithAccount = userAndAccountService.accountSignup(newAccount);
        }
        // 替換原來的 OAuth2Authentication
        return userWithAccount;
    }
        githubFilter.setAuthenticationSuccessHandler(new GithubAuthenticationSuccessHandler());

 

# 設置 Cookie

  1. 實現 UserDetailServices。
  2. 用 UserDetailServices 構造 RememberMeServices。這里采用的 RememberMeServices 的具體實現是 TokenBasedRememberMeServices。TokenBasedRememberMeServices 會用 UserDetailServices 構造 Authentication 的 Principal 對象。還有一個構造參數(key)是指定鹽,指定一個值,該值在應用反復重啟的時要保持不變。順便提一下,TokenBasedRememberMeServices 加密是根據 UserDetailServices 構造出的 UserDetails 中的 username、password、時間戳、構造參數 key 進行 MD5 和 Base64 加密和解密。
  3. 在 OAuth2ClientAuthenticationProcessingFilter 和繼承類 WebSecurityConfigurerAdapter 的方法 configure 注冊 RememberMeServices。注冊到 OAuth2ClientAuthenticationProcessingFilter 是為了在 OAuth2 認證成功或者失敗之后設置 Cookie。在 configure 注冊 RememberMeServices 是為了利用 Cookie 自動登錄。
public class CachingUserDetailsService implements UserDetailsService {

    private static final String USER_KEY = "user:%s";

    private StringRedisTemplate stringRedisTemplate;
    private UserAndAccountService userAndAccountService;

    public CachingUserDetailsService(StringRedisTemplate stringRedisTemplate, UserAndAccountService userAndAccountService) {
        this.stringRedisTemplate = stringRedisTemplate;
        this.userAndAccountService = userAndAccountService;
    }

    public UserDetails loadUserByUsername(String username) {
        String actualKey = String.format(USER_KEY, username);
        UserWithAccount userWithAccount;
        String userWithAccountJson = stringRedisTemplate.opsForValue().get(actualKey);
        if (StringUtils.isEmpty(userWithAccountJson)) {
            userWithAccount = userAndAccountService.getByUserId(Integer.parseInt(username));
            stringRedisTemplate.opsForValue().set(actualKey, new Gson().toJson(userWithAccount));
        } else {
            userWithAccount = new Gson().fromJson(userWithAccountJson, UserWithAccount.class);
        }
        return userWithAccount;
    }

}
    @Bean
    public RememberMeServices rememberMeServices() {
        TokenBasedRememberMeServices tokenBasedRememberMeServices = new TokenBasedRememberMeServices(REMEMBER_ME_KEY, userDetailsService());
        tokenBasedRememberMeServices.setAlwaysRemember(true);
        tokenBasedRememberMeServices.setTokenValiditySeconds(ONE_DAY_IN_SECONDS);
        return tokenBasedRememberMeServices;
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .rememberMe()
                .key(REMEMBER_ME_KEY)
                .rememberMeServices(rememberMeServices());
        http
                .addFilterBefore(oAuth2ClientAuthenticationProcessingFilter(), BasicAuthenticationFilter.class); // Github OAuth2 登錄的 Filter
    }

    @Bean
    public OAuth2ClientAuthenticationProcessingFilter oAuth2ClientAuthenticationProcessingFilter() {
        OAuth2ClientAuthenticationProcessingFilter githubFilter = new OAuth2ClientAuthenticationProcessingFilter("/security/oauth2/github");
        OAuth2RestTemplate facebookTemplate = new OAuth2RestTemplate(githubClient(), oauth2ClientContext);
        githubFilter.setAllowSessionCreation(false);
        githubFilter.setRestTemplate(facebookTemplate);
        githubFilter.setTokenServices(new GithubUserInfoTokenServices(githubResource().getUserInfoUri(), githubClient().getClientId(), userAndAccountService));
        githubFilter.setRememberMeServices(rememberMeServices());
        githubFilter.setAuthenticationSuccessHandler(new GithubAuthenticationSuccessHandler());
        return githubFilter;
    }

 

 

# 后記

 

我本來預估一周時間就把我項目的安全控制模塊給開發完,結果被 Spring Oauth2 拖了一周。由於對 Spring 核心概念也不是特別熟悉,看代碼也比較費事(雖然大部分看得懂)。最近一段時間准備寫一套 IoC 方面的代碼。

我現在所在的公司之前有個人挺厲害,自己寫了一套 IoC 容器用到了系統中。不過他也很坑,他寫的 IoC 容器出了問題,根本找不到任何解決資料,只能看源碼。自己寫的只能做玩具玩玩吧。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM