Password Credentials 核發流程


當授權過程涉及使用者,Password Credentials 核發流程是最簡單也容易理解的類型,使用者運用名稱、密碼來請求核發 Access Token,基本上是傳統驗證授權的延伸,運用 Password Credentials 核發流程的情境,通常是客戶端與服務屬於同一個單位,該單位本身就擁有使用者的註冊資訊,由於服務只認同 Access Token 就可以使用,也可用來實現 Single sign-on,也就是一次登入,就可使用各個獨立服務的功能。

想實作 Password Credentials 核發流程中的授權伺服器,起手式基本上與〈Client Credentials 核發流程(一)〉相同,然而,要結合已註冊的使用者名稱、密碼來進行驗證,例如:

package cc.openhome;

...略

@SpringBootApplication
@EnableAuthorizationServer
public class AuthSvrApplication {
    ...略

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Bean
    public WebSecurityConfigurerAdapter webSecurityConfig() {
          return new WebSecurityConfigurerAdapter() {
              @Override
              protected void configure(AuthenticationManagerBuilder builder) throws Exception {
                  builder.inMemoryAuthentication()
                         .passwordEncoder(passwordEncoder)
                         .withUser("caterpillar")
                             .password(passwordEncoder.encode("12345678"))
                             .roles("MEMBER");
              }
          };
    }   

    @Autowired
    @Qualifier("webSecurityConfig")
    private WebSecurityConfigurerAdapter webSecurityConfigurerAdapter;

    @Bean
    public AuthorizationServerConfigurer authorizationServerConfigurer() {
        return new AuthorizationServerConfigurerAdapter() {
            @Override
            public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
                clients.inMemory()
                       .withClient("memberclient")
                       .secret(passwordEncoder.encode("memberclient12345678"))
                       .scopes("message")
                       .resourceIds("resource")
                       .authorizedGrantTypes("password", "refresh_token");
            }

            @Override
            public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
                oauthServer.checkTokenAccess("isAuthenticated()");    
            }

            @Override
            public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
                endpoints.authenticationManager(webSecurityConfigurerAdapter.authenticationManagerBean())
                         .userDetailsService(webSecurityConfigurerAdapter.userDetailsServiceBean());
            }           
        };
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

在上頭可以看到,WebSecurityConfigurerAdapter 實例定義了註冊的使用者名稱與密碼(當然,實際上可能來自資料庫,或者是另一個使用者服務),而在 AuthorizationServerConfigurer 的定義中,定義了客戶端名稱 memberclient,密鑰 memberclient12345678,以及 scope、資源 ID 等。

在這邊 authorizedGrantTypes 定義了 password,這表示使用 Password Credentials 核發流程,至於 refresh_token,表示在核發 Access Token 時,也核發 Refresh Token,後者的有效期更長(預設 30 天),之後若 Refresh Token 過期時,可以直接使用 Refresh Token 來更新 Access Token,然而不需要再附上使用者名稱、密碼,這可以用來實作自動登入之類的功能,如果沒有定義 refresh_token,就不會核發 Refresh Token。

為了能驗證使用者名稱與密碼,需要透過 AuthorizationServerEndpointsConfigurerauthenticationManager 設定使用 WebSecurityConfigurerAdapterAuthenticationManager,而為了要能使用 Refresh Token 來更新 Access Token,要使用 userDetailsService 設定使用 WebSecurityConfigurerAdapteruserDetailsServiceBean

要請求核發 Access Token,同樣地,客戶端以 POST 指定 grant_typescope,以及 usernamepassword

Password Credentials 核發流程

你必須以 BASIC 驗證方式,告知授權伺服器客戶端名稱與密鑰:

Password Credentials 核發流程

在這邊可以看到 access_token 以及 refresh_token,若要使用 Refresh Token 來更新 Access Token,與請求核發類似,不過 grant_type 指定 refresh_token,並以 refresh_token 請求參數附上 Refresh Token:

Password Credentials 核發流程

同樣地,你可以如〈Client Credentials 核發流程(二)〉中的方式,拿著 Access Token 請求 oauth/check_token

Password Credentials 核發流程

在回傳的 JSON 中包含了使用者角色等相關資訊,資源伺服器若要取得這項資訊,可以透過 OAuth2AuthenticationgetPrincipal 來取得,例如修改〈Client Credentials 核發流程(二)〉中資源伺服器的 hello 方法:

@GetMapping("/hello")
public String hello(OAuth2Authentication oauth) {
    return "hello " + oauth.getPrincipal();
}

因為我們修改了客戶端 ID 與密鑰,因此記得 application.properties 也要改一下:

security.oauth2.client.clientId: memberclient
security.oauth2.client.clientSecret: memberclient12345678
security.oauth2.resource.token-info-uri: http://localhost:8081/oauth/check_token

底下是請求資源伺服器的 hello 結果:

Password Credentials 核發流程

你可以在 OAuth2PasswordGrant 找到以上的範例專案。