1.OAuth 2.0簡介
OAuth 2.0提供者機制負責公開OAuth 2.0受保護的資源。該配置包括建立可獨立或代表用戶訪問其受保護資源的OAuth 2.0客戶端。提供者通過管理和驗證用於訪問受保護資源的OAuth 2.0令牌來實現。在適用的情況下,提供商還必須提供用戶界面,以確認客戶端可以被授權訪問受保護資源(即確認頁面)。
2.OAuth 2.0的四種授權模式
OAuth 2.0常見的有如下四種授權模式,(主要參考自 阮一峰 OAuth 2.0 的四種方式):
- 授權碼(authorization code)方式,指的是第三方應用先申請一個授權碼,然后再用該碼獲取令牌.
- 隱藏式,有些 Web 應用是純前端應用,沒有后端。這時就不能用上面的方式了,必須將令牌儲存在前端。RFC 6749 就規定了第二種方式,允許直接向前端頒發令牌。這種方式沒有授權碼這個中間步驟,所以稱為(授權碼)"隱藏式"(implicit).
- 密碼式,如果你高度信任某個應用,RFC 6749 也允許用戶把用戶名和密碼,直接告訴該應用。該應用就使用你的密碼,申請令牌,這種方式稱為"密碼式"(password).
- 憑證式,最后一種方式是憑證式(client credentials),適用於沒有前端的命令行應用,即在命令行下請求令牌.
3.使用授權碼模式獲得JWTtoken令牌Demo項目演示
client_id: demo-client
client_secret: demo-secret (數據表中需要加密)
// 登錄頁訪問地址
http://localhost:8080/login
用戶名: admin
密碼: 123456
// 資源頁訪問地址
http://localhost:8081/oauth2-server/login
// 授權訪問地址
http://localhost:8080/oauth/authorize?client_id=demo-client&response_type=code&redirect_uri=http://localhost:8081/oauth2-server/login
Html演示頁面地址為:http://localhost:8081/oauth2-server/index,授權訪問效果如下,授權后會返回相應的token:
4.項目結構以及相關代碼配置
項目結構如下:
建表語句:
CREATE TABLE `oauth_client_details` (
`client_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`resource_ids` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`client_secret` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`scope` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`authorized_grant_types` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`web_server_redirect_uri` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`authorities` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`access_token_validity` int(11) NULL DEFAULT NULL,
`refresh_token_validity` int(11) NULL DEFAULT NULL,
`additional_information` varchar(4096) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`autoapprove` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`client_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `oauth_client_details` VALUES ('demo-client', NULL, '$2a$10$jr6DMjq2pRS1pa8vGvgdUewJTLXyHazOIgG5OrotSVhqhTgoCx1m.', 'all', 'authorization_code,refresh_token', 'http://localhost:8081/oauth2-server/login', NULL, 3600, 36000, NULL, '1');
pom依賴:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
<!-- ################## Lombok ################## -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.49</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
security-web: 用於用戶的認證,根據用戶的用戶名和密碼進行認證並用於獲得授權碼和獲取JWT token的服務,主要配置如下:
GetSecret (獲取Header以及加密后的密碼):
public class GetSecret {
private static final String APP_KEY = "demo-client";
private static final String SECRET_KEY = "demo-secret";
public static void main(String[] args){
System.out.println("secret: "+new BCryptPasswordEncoder().encode("demo-secret"));
System.out.println("getHeader: "+getHeader());
}
/**
* 構造Basic Auth認證頭信息
*
* @return
*/
private static String getHeader() {
String auth = APP_KEY + ":" + SECRET_KEY;
byte[] encodedAuth = Base64.encodeBase64(auth.getBytes(Charset.forName("US-ASCII")));
String authHeader = "Basic " + new String(encodedAuth);
return authHeader;
}
}
WebSecurityConfig
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* 允許匿名訪問所有接口 主要是 oauth 接口
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.and()
.authorizeRequests()
.antMatchers("/**").permitAll();
}
}
OAuth2Config
@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
@Qualifier("demoUserDetailsService")
public UserDetailsService demoUserDetailsService;
@Autowired
private DataSource dataSource;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private TokenStore jwtTokenStore;
@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
@Autowired
private TokenEnhancer jwtTokenEnhancer;
@Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
/**
* jwt 增強模式
*/
TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
List<TokenEnhancer> enhancerList = new ArrayList<>();
enhancerList.add(jwtTokenEnhancer);
enhancerList.add(jwtAccessTokenConverter);
enhancerChain.setTokenEnhancers(enhancerList);
endpoints.tokenStore(jwtTokenStore)
.userDetailsService(demoUserDetailsService)
/**
* 支持 password 模式
*/
.authenticationManager(authenticationManager)
.tokenEnhancer(enhancerChain)
.accessTokenConverter(jwtAccessTokenConverter);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
}
oauth2-server: 主要用於授權認證后的資源訪問,使用的是授權碼授權模式(這也是最常見的Oauth2.0的模式),主要資源配置如下:
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Bean
public TokenStore jwtTokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
accessTokenConverter.setSigningKey("demo");
accessTokenConverter.setVerifierKey("demo");
return accessTokenConverter;
}
@Autowired
private TokenStore jwtTokenStore;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenStore(jwtTokenStore);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/login").permitAll();
}
}
5.GitHub項目地址:
https://github.com/fengcharly/spring-boot-oauth2