前言
爬了兩天的坑終於上來了,看別人寫的很簡單,自己寫的時候就各種問題,網上SSO文章很多,寫的時候就各種問題(自己太菜了)。記錄一下,以後方便查看。
關於OAuth2認證和授權服務器見上一篇
https://zheyday.github.io/2019/10/07/SpringCloud-OAuth2和JWT/
項目地址:
https://github.com/zheyday/OAuth2
參考資料:
https://www.cnblogs.com/cjsblog/p/10548022.html
阮一峯OAuth2講解
https://blog.csdn.net/WSM960921/article/details/98222004
什麼是SSO
英文全稱single Sign On,中文名單點登錄。就是在多應用系統中,用戶只需要登錄一次就可以訪問所有信任服務。SSO通過將用戶登陸信息映射到瀏覽器cookie中,解決其他服務免登陸獲取用戶session的問題。
大體框架
項目中一個有5個模塊:
-
oauth-server是認證和授權服務,負責令牌的發放
-
zuul是網關服務,實現統一授權
-
eureka-client和eureka-client1是兩個應用服務
-
eureka-server-single是eureka註冊中心
要實現的功能就是通過zuul端口訪問eureka-client,首次需要登錄,然後內部跳轉到oauth-server中進行認證和授權,成功之後也可以不用登錄訪問eureka-client1。
oauth-server
更改了幾個地方
AuthorizationServerConfig
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("zuul")
.secret(new BCryptPasswordEncoder().encode("secret"))
.scopes("app")
.authorizedGrantTypes("authorization_code", "password")
.redirectUris("http://localhost:9110/login")
;
// clients.withClientDetails(new JdbcClientDetailsService(dataSource));
}
UserController
這個函數可以爲其他模塊資源服務器提供校驗
@RestController
@RequestMapping("/oauth2_token")
public class UserController {
@GetMapping("/current")
public Principal user(Principal principal) {
return principal;
}
}
ResourceServerConfig
@Override
public void configure(HttpSecurity http) throws Exception {
http
// antMatcher表示只能處理/oauth2_token的請求
.antMatcher("/oauth2_token/**")
.authorizeRequests()
.anyRequest().authenticated()
;
}
application.yml
添加了根地址,也就是每次訪問這個服務都要加上 /oauth-server
server:
port: 9120
# url根地址 不配置的話會報invalid_token錯誤
# 或者在zuul中配置也可以
servlet:
context-path: /oauth-server
Zuul
zuul作爲系統的入口,提供路由、統一授權等功能。
依賴見項目中
注意:所有模塊都引入了spring-cloud-starter-oauth2依賴,所以放入了項目的公共pom.xml中,實現oauth2只需要引用這一個就可以
application.yml
貼出主要配置,詳細見項目中
sensitiveHeaders:
這個必須要。zuul在轉發路由時,會改寫request中的頭部信息,設置成空就是不過濾
security.oauth2.resource:這個是和解析令牌相關的配置
資源服務器需要解析令牌驗證正確性,方式有三種:
- 如果令牌不是jwt非對稱加密,那麼訪問/oauth/check_token直接驗證token;
如果是非對稱,訪問/oauth/token_key獲得公鑰進行解析 - 訪問認證服務器中的controller中的方法,獲得Principal進行驗證
- 在資源服務器本地配置,繼承ResourceServerConfigurerAdapter接口,實現和認證服務器相同的加密方式
zuul:
routes:
eureka-client:
path: /eureka-client/**
sensitiveHeaders:
serviceId: eureka-client
eureka-client1:
path: /eureka-client1/**
sensitiveHeaders:
serviceId: eureka-client1
oauth-server:
path: /oauth-server/**
sensitiveHeaders:
serviceId: eureka-client
#認證服務器地址
oauth-server: http://localhost:9120/oauth-server
security:
oauth2:
# 和認證服務器中的client設置對應
client:
client-id: zuul
client-secret: secret
# 獲取令牌地址
access-token-uri: ${oauth-server}/oauth/token
# 認證地址
user-authorization-uri: ${oauth-server}/oauth/authorize
resource:
# 進行令牌校驗
# 一、訪問controller獲取Principal
# user-info-uri: ${oauth-server}/oauth2_token/current
# prefer-token-info: false
# 二、訪問授權服務器獲取公鑰 解析令牌
jwt:
key-uri: ${oauth-server}/oauth/token_key
配置WebSecurityConfig
整個zuul模塊只要添加這一個配置就可以
@EnableOAuth2Sso
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
//需要授權的url
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and().csrf().disable()
;
}
}
eureka-client(資源服務器)
application.yml
新添如下,用於驗證請求的令牌是否正確
security:
oauth2:
resource:
id: eureka-client
# 資源服務器三種驗證令牌方法
# 一、在ResourceServerConfig中本地配置
# 二、從認證服務器獲取用戶信息 解析出token
# user-info-uri: ${oauth-server}/oauth2_token/current
# prefer-token-info: false
# 三、遠程獲取公鑰 解析token
jwt:
key-uri: ${oauth-server}/oauth/token_key
配置ResourceServerConfig
資源服務器只需要添加這一個配置即可
@Configuration
@EnableResourceServer
//Spring Security默認禁用註解 這裏開啓註解
// 結合@PreAuthorize("hasRole('admin')")用來判斷用戶對某個控制層的方法是否有訪問權限
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
// 配置需要權限的url
.authorizeRequests()
.anyRequest().authenticated()
.and().csrf().disable();
;
}
//以下就是驗證令牌的本地方法 如果使用這種,將上述application.yml的配置註釋掉,同時將oauth2.jks放入resources文件夾
// @Override
// public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
// resources.resourceId("app").tokenStore(tokenStore());
// }
//
// @Bean
// public TokenStore tokenStore() {
// return new JwtTokenStore(jwtAccessTokenConverter());
// }
//
// /**
// * 非對稱加密算法對token進行簽名
// *
// * @return
// */
// @Bean
// public JwtAccessTokenConverter jwtAccessTokenConverter() {
// final JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
// // 導入證書
// KeyStoreKeyFactory keyStoreKeyFactory =
// new KeyStoreKeyFactory(new ClassPathResource("oauth2.jks"), "mypass".toCharArray());
// converter.setKeyPair(keyStoreKeyFactory.getKeyPair("oauth2"));
// return converter;
// }
}
注意:
如果使用本地驗證方法,可能會報讀取不到oauth2.jks的錯誤,因爲默認不引用resources目錄下的文件,需要在pom.xml中<build>下添加配置
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
測試
啓動順序:
- eureka-server-single
- oauth-server
- zuul
- eureka-client和eureka-ciient1
通過zuul端口訪問eureka-client下的方法 http://localhost:9110/eureka-client/hi
會自動跳到oauth-server的登陸頁面,輸入用戶名和密碼登陸
看url,跳轉到了oauth/authorize,這是授權接口,並且後面攜帶了一些zuul配置的信息
成功
訪問eureka-client1不需要登陸
總結
寫的比較簡潔,但是做出來真的花費了兩天的時間精力(還是太菜了)
沒有什麼文采,原理性的東西也寫不出來,只能記錄一下實踐,大家共同交流
更多文章見個人博客 https://zheyday.github.io/