概述
??OAuth 2.0是用于授權的行業(yè)標準協(xié)議,允許用戶授權第三方應用訪問他們存儲在其他服務上的信息,而不需要將用戶名和密碼提供給第三方應用。OAuth 2.0取代了在2006年創(chuàng)建的原始OAuth協(xié)議,是OAuth協(xié)議的延續(xù)版本,但不向后兼容。
一、OAuth2 角色
- 資源所有者 ( 用戶 )
- 客戶端 ( 第三方應用 )
- 授權服務器 ( 對客戶端發(fā)送的請求信息進行驗證并返回token的服務器 )
- 資源服務器 ( 提供用戶資源的服務器 )
授權服務器和資源服務器可以是同一臺服務器
二、授權類型
-
authorization_code授權碼模式:是功能最完整、流程最嚴密的授權模式。它的特點就是通過客戶端服務器與服務端服務器交互,常見的第三方平臺登錄功能基本使用這種模式。 -
implicit簡化模式:不需要客戶端服務器參與,直接通過瀏覽器向授權服務器申請令牌。 -
password密碼模式:用戶將賬號和密碼直接告訴第三方客戶端,客戶端使用這些信息向授權服務器申請令牌(需要用戶對客戶端高度信任)。 -
client_credentials客戶端模式:客戶端使用自身向授權服務器申請授權,不需要用戶參與。
三、實踐
??以下實例主要介紹密碼模式
授權服務器
在application.yml中配置redis
spring:
redis:
host: xxx.xxx.xxx.xxx
port: 6379
創(chuàng)建 WebSecurityConfigure類:
@Configuration
public class WebSecurityConfigure extends WebSecurityConfigurerAdapter {
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
@Override
protected UserDetailsService userDetailsService() {
return super.userDetailsService();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 基于內(nèi)存的認證
auth.inMemoryAuthentication()
.withUser("user")
.password(new BCryptPasswordEncoder().encode("123456"))
.roles("USER")
.and()
.withUser("ruoshy")
.password(new BCryptPasswordEncoder().encode("123456"))
.roles("ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/oauth/**")
.permitAll()
.and()
.csrf().disable();
}
}
該類將 AuthenticationManager,UserDetailsService添加到bean在OAuth配置類中進行配置,并配置基于內(nèi)存的用戶和請求攔截。
創(chuàng)建 OAuthSecurityConfigure類:
@Configuration
@EnableAuthorizationServer
public class OAuthSecurityConfigure extends AuthorizationServerConfigurerAdapter {
@Resource
private AuthenticationManager authenticationManager;
@Resource
private RedisConnectionFactory redisConnectionFactory;
@Resource
private UserDetailsService userDetailsService;
/**
* 配置編碼器
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()").checkTokenAccess("permitAll()")
.allowFormAuthenticationForClients();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory() // 基于內(nèi)存
.withClient("app") //授權客戶端
.secret(passwordEncoder().encode("app-secret")) //授權碼
.accessTokenValiditySeconds((int) TimeUnit.HOURS.toSeconds(1)) // 授權過期時間
.authorizedGrantTypes("password", "refresh_token") // 授權模式
.scopes("all") // 授權范圍
.resourceIds("rid"); // 授權資源
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(new RedisTokenStore(redisConnectionFactory)) // token存儲倉庫
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
}
完成以上配置,授權服務器就簡單的完成了。
資源服務器
在application.yml中配置:
security:
oauth2:
client:
user-authorization-uri: http://localhost:8080/oauth/authorize
access-token-uri: http://localhost:8080/oauth/token
client-secret: app-secret
client-id: app
resource:
token-info-uri: http://localhost:8080/oauth/check_token
server:
port: 8081
創(chuàng)建 ResourceConfigure類:
@EnableResourceServer
@Configuration
public class ResourceConfigure extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("rid");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/admin/**").hasRole("ADMIN");
}
}
以上資源服務器也簡單的完成了
創(chuàng)建Controller類進行測試:
@RestController
public class ResourceController {
@RequestMapping("/user")
public String user() {
return "user";
}
@RequestMapping("/admin")
public String admin() {
return "admin";
}
}
請求授權服務器獲取Token(access_token):

使用ADMIN權限賬戶請求資源服務器:
http://localhost:8081/admin?access_token=23739aad-58ba-4c65-8f4c-9f1d96d87f46

http://localhost:8081/user?access_token=23739aad-58ba-4c65-8f4c-9f1d96d87f46

當然資源服務器和授權服務器可以為同一臺,可以將以上資源服務器的資源配置類和Controller類添加到授權服務器上(application.yml不需要)即可實現(xiàn)相同的功能。
四、基于數(shù)據(jù)庫的認證
配置用戶基于數(shù)據(jù)庫認證可以參閱該篇文章:
http://m.itdecent.cn/p/a1d47c1d2be4
(配置用戶基于數(shù)據(jù)庫認證需要刪除以上WebSecurityConfigure類中配置的userDetailsService()方法)
配置客戶端信息服務配置基于數(shù)據(jù)庫:
數(shù)據(jù)庫SQL
CREATE TABLE `oauth_client_details` (
`client_id` varchar(20) NOT NULL,
`resource_ids` varchar(256) DEFAULT NULL,
`client_secret` varchar(255) DEFAULT NULL,
`scope` varchar(255) DEFAULT NULL,
`authorized_grant_types` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
`web_server_redirect_uri` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
`authorities` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
`access_token_validity` int(11) DEFAULT NULL,
`refresh_token_validity` int(11) DEFAULT NULL,
`autoapprove` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
`additional_information` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

在application.yml中配置mysql并在以上OAuthSecurityConfigure類中注入DataSource將configure(ClientDetailsServiceConfigurer clients)方法中基于內(nèi)存的認證修改為:
@Resource
private DataSource dataSource;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
完成以上步驟就可以通過數(shù)據(jù)庫來動態(tài)的管理授權信息了。