什麼是oAuth2
簡單來說,就是讓一個系統可以直接訪問另一個系統,通過授權碼。
與單點登錄的區別
什麼是單點登錄: 用戶成功登錄A系統,需要訪問B系統時,可以不用輸入用戶名密碼進行認證就可以直接訪問B系統的資源。
什麼是oauth2: A系統徵求用戶的同意後直接訪問B系統,用戶不需要登錄也不需要訪問B系統。
四種授權模式
1)授權碼模式(oauth2 中最安全最完善的一種模式,應用場景最廣泛)
A系統要訪問B系統的資源,A系統詢問用戶是否同意其訪問B系統的資源。如果用戶同意,則B系統生成授權碼給A,A系統帶着授權碼向B系統發送請求,並獲取到令牌 token 和更新令牌 refresh token。(前提:用戶有訪問B系統的權限)
2)簡化模式
跟授權碼模式類似,去掉了授權碼。直接獲得B系統給的 token ,通過 token 訪問B系統
3)密碼模式
用戶直接把自己訪問B系統的賬號密碼告訴 A系統,A系統拿着用戶的密碼去訪問B系統。
4)客戶端模式
A系統直接脫離用戶,以自己的身份去B系統獲取 token,可以說完全是 AB 系統內部的交互,與用戶無關了。該模式不太屬於 oauth2 範疇
OAuth2表結構
oAuth2 自帶有 7 張表
注意:這裏的 sql 默認是 HSQLDB 的,我們用 mysql 的可以把 LONGVARBINARY 數據類型改成 BLOB
oauth_client_details
是核心表,後面測試時只要看這張表即可
表結構字段說明可參考博客 https://blog.csdn.net/qq_34997906/article/details/89609297
創建父工程
本文只貼 配置類代碼,其他具體代碼跟上一篇博客類似,具體可查看文末源碼
注意 spring cloud 的版本匹配,本例用的 springboot2.3,可參考 https://start.spring.io/actuator/info
創建認證模塊
即 B 系統的 oauth2 認證模塊,B系統需要檢測用戶是否登錄,用戶成功登錄了B系統才能授權給A系統去訪問
@Configuration
@EnableWebSecurity
public class SpringsecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private SysUserService userService;
//配置加密
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); //spring security 內置加密算法
}
//認證用戶的來源
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
}
//Spring Security配置
public void configure(HttpSecurity hs) throws Exception {
hs.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/login")
.permitAll()
.and()
.csrf()
.disable();
}
// 對象在oauth2認證服務中要使用,注意方法名不要亂改
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
@Configuration
@EnableAuthorizationServer
public class oauthServerConfig extends AuthorizationServerConfigurerAdapter {
//數據庫對象
@Autowired
private DataSource dataSource;
//認證業務對象
@Autowired
private SysUserService userService;
//授權模式專用對象
@Autowired
private AuthenticationManager authenticationManager;
//客戶端信息來源
@Bean
public JdbcClientDetailsService jdbcClientDetailsService() {
return new JdbcClientDetailsService(dataSource);
}
//token保存策略
@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
//授權信息保存策略
@Bean
public ApprovalStore approvalStore() {
return new JdbcApprovalStore(dataSource);
}
//授權碼模式數據來源
@Bean
public AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource);
}
//指定客戶端信息的數據來源
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(jdbcClientDetailsService());
}
//驗證token的策略
public void configure(AuthorizationServerSecurityConfigurer securityConfigurer) {
securityConfigurer.allowFormAuthenticationForClients();
securityConfigurer.checkTokenAccess("isAuthenticated()");
}
//oauth2的主配置
public void configure(AuthorizationServerEndpointsConfigurer endpointsConfigurer) {
endpointsConfigurer.approvalStore(approvalStore())
.authenticationManager(authenticationManager)
.authorizationCodeServices(authorizationCodeServices())
.tokenStore(tokenStore());
}
}
創建資源模塊
即 B 系統的資源服務
@Configuration
@EnableResourceServer
public class OauthConfig extends ResourceServerConfigurerAdapter {
@Autowired
private DataSource dataSource;
/**
* 指定Token的持久化策略
* InMemoryTokenStore 表示將 token 存儲在內存
* Redis 表示將 token 存儲在Redis
* JdbcTokenStore 表示將 token 存儲在數據庫
* @return
*/
@Bean
public TokenStore jdbcTokenStore() {
return new JdbcTokenStore(dataSource);
}
/**
* 指定當前資源的id和存儲方案
* @param resource
* @throws Exception
*/
public void configure(ResourceServerSecurityConfigurer resource) throws Exception {
resource.resourceId("source_api").tokenStore(jdbcTokenStore());
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//指定不同請求方式訪問資源所需的權限
.antMatchers(HttpMethod.GET, "/**").access("#oauth2.hasScope('read')")
.antMatchers(HttpMethod.POST, "/**").access("#oauth2.hasScope('write')")
.antMatchers(HttpMethod.PATCH, "/**").access("#oauth2.hasScope('write')")
.antMatchers(HttpMethod.PUT, "/**").access("#oauth2.hasScope('write')")
.antMatchers(HttpMethod.DELETE, "/**").access("#oauth2.hasScope('write')")
.and()
.headers().addHeaderWriter((request, response) -> {
response.addHeader("Access-Control-Allow_Origin", "*");//允許跨域
//如果是跨域的預檢請求,則原封不動向下傳達請求頭信息
if(request.getMethod().equals("OPTIONS")) {
response.setHeader("Access-Control-Allow-Methods", request.getHeader("Access-Control-Allow-Methods"));
response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Allow-Headers"));
}
});
}
}
測試
1)添加測試數據(A系統在B系統註冊的客戶端信息)
B系統需要保存A系統的id,A系統可以訪問哪些資源,都有什麼權限,訪問的模式,回調地址,是否需要用戶同意
2)啓動兩個模塊
注意請求路徑的寫法,是oauth2提供的,我們只需改變參數
-
首先在網頁上訪問:http://localhost:8081/oauth/authorize?response_type=code&client_id=client01
去獲取授權碼,這裏需要用戶輸入賬號密碼登錄
-
打開 postman 訪問:http://localhost:8081/oauth/token,在 body 中加入如下參數
grant_type
client_id
client_secret
code
username
password
本項目 GitHub 地址:https://github.com/godXiaogf/springsecurity_oauth2