OAuth2.0 最直觀配置

spring 全家桶已經實現了 OAuth2.0 的全部功能(spring-cloud-starter-security、spring-security-oauth2-autoconfigure),我們只要引用對應庫就可以了。

表結構

無論哪種認證方式,總要把數據存儲起來(無論是在緩存還是 DB),OAuth2.0 依賴的數據一般是存放在 DB 中的,全部表結構可以參考這裡

其中不能省的表就這一個:oauth_client_details(客戶端信息配置,當然了表名可以自己隨便改)


OAuth2.0 最直觀配置


其它用戶表、角色權限表、token 存儲表等等都可以用我們自己的業務表來做,token 可以存放在 redis 中。

數據結構

用戶信息

org.springframework.security.core.userdetails.User

<code>private String password;
private final String username;
private final Set<grantedauthority> authorities;
private final boolean accountNonExpired;
private final boolean accountNonLocked;
private final boolean credentialsNonExpired;
private final boolean enabled;/<grantedauthority>/<code>

這個類可以自定義,繼承 User 就好了,一般都是用項目中的用戶類。

權限信息

可以是菜單權限,也可以數據權限,這個完全根據業務來自定義。

比如使用業務系統的角色 + 權限關係表,就可以知道當前用戶能操作哪些菜單 / 數據(Set authorities)。

加密方式(針對 password 模式)

認證過程中提交的密碼不能是明文,如果不用默認的加密方式,可以自定義(必須與數據庫中存儲密碼的加密方式匹配)。

<code>@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
/<code>
<code>@Configuration
@AllArgsConstructor
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
\tprivate final DataSource dataSource;
\tprivate final UserDetailsService userDetailsService;
\tprivate final AuthenticationManager authenticationManager;
// 用戶信息和token存放在redis中
\tprivate final RedisConnectionFactory redisConnectionFactory;

\t@Override
\t@SneakyThrows
\tpublic void configure(ClientDetailsServiceConfigurer clients) {
// 自定義用戶查詢實現類
\t\tMyClientDetailsService clientDetailsService = new MyClientDetailsService(dataSource);
// 自定義查詢oauth_client_details數據的sql(根據client_id查詢)
\t\tclientDetailsService.setSelectClientDetailsSql("select * from ...");
// 自定義查詢所有oauth_client_details數據的sql
\t\tclientDetailsService.setFindClientDetailsSql("select * from ...");
\t\tclients.withClientDetails(clientDetailsService);
\t}

\t@Override
\tpublic void configure(AuthorizationServerSecurityConfigurer oauthServer) {
\t\toauthServer
// 讓 /oauth/token 支持 client_id 以及 client_secret 作登錄認證
\t\t\t.allowFormAuthenticationForClients()
// 允許可訪問 /oauth/check_token 對應的api
\t\t\t.checkTokenAccess("permitAll()");
\t}

\t@Override
\tpublic void configure(AuthorizationServerEndpointsConfigurer endpoints) {
\t\tendpoints
// oauth請求使用get或put方式
\t\t\t.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
// token存儲方式

\t\t\t.tokenStore(tokenStore())
// 自定義token中附加信息的內容
\t\t\t.tokenEnhancer(tokenEnhancer())
// 獲取用戶信息的實現類
\t\t\t.userDetailsService(userDetailsService)
\t\t\t.authenticationManager(authenticationManager)
// 是否重複使用refreshToken直到過期
\t\t\t.reuseRefreshTokens(false)
// 自定義授權確認api
\t\t\t.pathMapping("/oauth/confirm_access", "/token/confirm_access")
// 自定義異常處理類
\t\t\t.exceptionTranslator(new MyWebResponseExceptionTranslator());
\t}

\t@Bean
\tpublic TokenStore tokenStore() {
\t\tRedisTokenStore tokenStore = new RedisTokenStore(redisConnectionFactory);
\t\ttokenStore.setPrefix("myOAuth2.0");
\t\treturn tokenStore;
\t}

\t@Bean
\tpublic TokenEnhancer tokenEnhancer() {
\t\treturn (accessToken, authentication) -> {
\t\t\tfinal Map<string> additionalInfo = new HashMap<>();
\t\t\tadditionalInfo.put("addInfo", "額外信息");
\t\t\t((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
\t\t\treturn accessToken;
\t\t};
\t}
}
/<string>/<code>

授權服務器正確分配了 token 後,在後續的請求過程中,資源服務器怎麼從 token 上獲取用戶信息呢?這就需要在資源服務器中配置了。

自定義資源服務器配置

<code>public class MyResourceServerConfigurerAdapter extends ResourceServerConfigurerAdapter {
// 自定義異常處理類

\t@Autowired
\tprotected ResourceAuthExceptionEntryPoint resourceAuthExceptionEntryPoint;
// 驗證token是否有效,默認即可,無需自定義
\t@Autowired
\tprotected RemoteTokenServices remoteTokenServices;
// 自定義拒絕授權處理器
\t@Autowired
\tprivate AccessDeniedHandler myAccessDeniedHandler;
// 允許匿名訪問的資源,支持ant通配符
\t@Autowired
\tprivate List<string> permitUrls;
// rest請求模板,默認即可
\t@Autowired
\tprivate RestTemplate restTemplate;

\t@Override
\t@SneakyThrows
\tpublic void configure(HttpSecurity httpSecurity) {
\t\tExpressionUrlAuthorizationConfigurer<httpsecurity>
\t\t\t.ExpressionInterceptUrlRegistry registry = httpSecurity
\t\t\t.authorizeRequests();
// 可匿名訪問資源
\t\tpermitUrls
\t\t\t.forEach(url -> registry.antMatchers(url).permitAll());
// 所有頁面需要授權
\t\tregistry.anyRequest().authenticated()
\t\t\t.and().csrf().disable();
\t}

\t@Override
\tpublic void configure(ResourceServerSecurityConfigurer resources) {
\t\tDefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
// 關鍵位置,根據check_token返回的結果,轉換為資源服務器中可用的用戶信息
\t\tUserAuthenticationConverter userTokenConverter = new MyUserAuthenticationConverter();
\t\taccessTokenConverter.setUserTokenConverter(userTokenConverter);

\t\tremoteTokenServices.setRestTemplate(restTemplate);
\t\tremoteTokenServices.setAccessTokenConverter(accessTokenConverter);
\t\tresources.authenticationEntryPoint(resourceAuthExceptionEntryPoint)
\t\t\t.accessDeniedHandler(myAccessDeniedHandler)
\t\t\t.tokenServices(remoteTokenServices);

\t}
}
/<httpsecurity>/<string>/<code>

這裡的 UserAuthenticationConverter 會根據 check_token 返回的內容來生成一個 org.springframework.security.core.userdetails.User 並把它放在 org.springframework.security.authentication.UsernamePasswordAuthenticationToken 中,這樣後面的處理邏輯都能從權限管理器中獲取到用戶信息了。

那 check_token 是怎麼獲取的 token 信息呢?

之前我們不是配置了 token 在 redis 中存儲麼?

<code>@Bean
public TokenStore tokenStore() {
RedisTokenStore tokenStore = new RedisTokenStore(redisConnectionFactory);
tokenStore.setPrefix("myOAuth2.0");
return tokenStore;
}
/<code>

在 RedisTokenStore 中你會看到是如何反序列化出來的

<code>@Override
public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) {
return readAuthenticationForRefreshToken(token.getValue());
}

public OAuth2Authentication readAuthenticationForRefreshToken(String token) {
RedisConnection conn = getConnection();
try {
byte[] bytes = conn.get(serializeKey(REFRESH_AUTH + token));
OAuth2Authentication auth = deserializeAuthentication(bytes);
return auth;
} finally {
conn.close();
}
}
/<code>

redis 中存儲的內容如下,其實就是一個序列化的 org.springframework.security.oauth2.provider.OAuth2Authentication 對象

OAuth2.0 最直觀配置

到這裡,Oauth2.0 的配置就很清楚了吧,後面我們再把 串起來,就更直觀了!


分享到:


相關文章: