(八)springcloud Oauth2授权- 四种授权类型
client_credentials
1、创建应用资源
@SpringBootApplication
public class ClientOAuth2Application {
public static void main(String[] args) {
SpringApplication.run(ClientOAuth2Application.class, args);
}
@RestController
@RequestMapping("/api")
class DemoController {
// 可以匿名访问
@GetMapping("/public/{id}")
public String publicResource(@PathVariable long id) {
return "this is public "+id;
}
// 必须经过认证
@GetMapping("/protect/{id}")
public String protectResource(@PathVariable long id) {
return "this is protect "+id;
}
}
}
2、配置认证服务器
//提供/oauth/authorize,/oauth/token,/oauth/check_token,/oauth/confirm_access,/oauth/error
@Configuration
@EnableAuthorizationServer
public class OAuth2ServerConfig extends AuthorizationServerConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
//allow check token
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("demoApp")
.secret(passwordEncoder().encode("demoAppSecret"))
.authorizedGrantTypes("client_credentials", "password", "refresh_token")
.scopes("all")
.resourceIds("oauth2-resource")
.accessTokenValiditySeconds(1200)
.refreshTokenValiditySeconds(50000);
}
}
3、配置资源访问控制
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
/**
* 配置资源访问控制
*/
@Override
public void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.requestMatchers().anyRequest()
.and()
.anonymous()
.and()
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.antMatchers("/api/protect/**").access("#oauth2.hasScope('all')")
.anyRequest().authenticated();
}
}
4、验证
1、启动应用,访问资源:
zuizuideMacBook-Pro:~ zuizui$ curl -i http://localhost:8080/api/public/1
HTTP/1.1 200
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: text/plain;charset=UTF-8
Content-Length: 16
Date: Sun, 05 May 2019 05:40:52 GMT
this is public 1
zuizuideMacBook-Pro:~ zuizui$ curl -i http://localhost:8080/api/protect/1
HTTP/1.1 401
Set-Cookie: JSESSIONID=3F3B351204A64E7788A2D988F6F9C53D; Path=/; HttpOnly
Cache-Control: no-store
Pragma: no-cache
WWW-Authenticate: Bearer realm="oauth2-resource", error="unauthorized", error_description="Full authentication is required to access this resource"
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
X-Frame-Options: DENY
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 05 May 2019 05:41:29 GMT
{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}
2、获取token
zuizuideMacBook-Pro:~ zuizui$ curl -i demoApp:demoAppSecret@localhost:8080/oauth/token -d grant_type=client_credentials
HTTP/1.1 200
Cache-Control: no-store
Pragma: no-cache
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
X-Frame-Options: DENY
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 05 May 2019 05:43:40 GMT
{"access_token":"2c24ea79-5e80-43a8-844f-458193ce3173","token_type":"bearer","expires_in":1199,"scope":"all"}
3、携带token访问资源
zuizuideMacBook-Pro:~ zuizui$ curl -i -H "Authorization:Bearer 2c24ea79-5e80-43a8-844f-458193ce3173" localhost:8080/api/protect/1
HTTP/1.1 200
Set-Cookie: JSESSIONID=F3208F6C579CF2F0456F52D070C34B59; Path=/; HttpOnly
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: text/plain;charset=UTF-8
Content-Length: 17
Date: Sun, 05 May 2019 05:45:32 GMT
this is protect 1
resource owner password credentials
密码模式需要配置
UserDetailsService
和AuthenticationManager
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
1、准备资源
@SpringBootApplication
public class OAuth2PasswordCredentialsApplication {
public static void main(String[] args) {
SpringApplication.run(OAuth2PasswordCredentialsApplication.class, args);
}
@RestController
@RequestMapping("/api")
class DemoController {
@GetMapping("/public/{id}")
public String publicResource(@PathVariable long id) {
return "this is public "+id;
}
@GetMapping("/protect/{id}")
public String protectResource(@PathVariable long id) {
return "this is protect "+id;
}
}
}
2、认证服务器配置
@Configuration
@EnableAuthorizationServer
public class OAuth2ServerConfig extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
/**
* 注入authenticationManager
* 来支持 password grant type
*/
@Resource
private AuthenticationManager authenticationManager;
@Resource
private PasswordEncoder passwordEncoder;
@Bean
public InMemoryTokenStore tokenStore() {
return new InMemoryTokenStore();
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.tokenStore(tokenStore());
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("demoApp")
.secret(passwordEncoder.encode("demoAppSecret"))
.authorizedGrantTypes("client_credentials", "password", "refresh_token")
.scopes("all")
.resourceIds("oauth2-resource")
.accessTokenValiditySeconds(1200)
.refreshTokenValiditySeconds(50000);
}
}
3、资源服务器配置
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
/**
* 要正常运行,需要反注释掉这段,具体原因见下面分析
* 这里设置需要token验证的url
* 这些需要在WebSecurityConfigurerAdapter中排查掉
* 否则优先进入WebSecurityConfigurerAdapter,进行的是basic auth或表单认证,而不是token认证
*/
@Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers().antMatchers("/api/**")
.and()
.authorizeRequests()
.antMatchers("/api/**").authenticated();
}
public static void main(String[] args) {
String encode = Base64.getEncoder().encodeToString("demoApp:demoAppSecret".getBytes());
System.out.println(encode);
}
}
4、WebSecurity配置
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.requestMatchers().antMatchers("/oauth/**")
.and()
.authorizeRequests()
.antMatchers("/oauth/**").authenticated();
}
//配置内存模式的用户
@Bean
@Override
protected UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("demoUser1").password(passwordEncoder()
.encode("123456")).authorities("USER").build());
manager.createUser(User.withUsername("demoUser2").password(passwordEncoder()
.encode("123456")).authorities("USER").build());
return manager;
}
/**
* 一定要将 userDetailsService 设置到 AuthenticationManagerBuilder 中
* 不然后面校验ClientDetailsService时会找不到UsernamePasswordToken的Provider
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
}
/**
* 需要配置这个支持password模式
* support password grant type
*/
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
校验资源
zuizuideMacBook-Pro:~ zuizui$ curl -i localhost:8080/api/public/1
HTTP/1.1 200
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: text/plain;charset=UTF-8
Content-Length: 16
Date: Sun, 05 May 2019 06:15:16 GMT
this is public 1
zuizuideMacBook-Pro:~ zuizui$ curl -i localhost:8080/api/protect/1
HTTP/1.1 401
Set-Cookie: JSESSIONID=EB613FD5D191BAE6AAF2F20FCC1358B6; Path=/; HttpOnly
Cache-Control: no-store
Pragma: no-cache
WWW-Authenticate: Bearer realm="oauth2-resource", error="unauthorized", error_description="Full authentication is required to access this resource"
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
X-Frame-Options: DENY
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 05 May 2019 06:15:53 GMT
{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}
获取token
zuizuideMacBook-Pro:~ zuizui$ curl -H "Authorization:Basic ZGVtb0FwcDpkZW1vQXBwU2VjcmV0" -i -d "grant_type=password&scope=all&username=demoUser1&password=123456" http://localhost:8080/oauth/token
HTTP/1.1 200
Cache-Control: no-store
Pragma: no-cache
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
X-Frame-Options: DENY
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 06 May 2019 01:35:06 GMT
{"access_token":"db51190d-eaa1-44f8-b61f-dbd97b5f401d","token_type":"bearer","refresh_token":"aef12b97-a2d6-48cd-afba-d88c5e0ad51f","expires_in":1088,"scope":"all"}
通过token访问资源
zuizuideMacBook-Pro:~ zuizui$ curl -i http://localhost:8080/api/protect/111 -H "Authorization: Bearer db51190d-eaa1-44f8-b61f-dbd97b5f401d"
HTTP/1.1 200
Set-Cookie: JSESSIONID=B793E39D6562AD2DDA09E04CF849E1DA; Path=/; HttpOnly
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: text/plain;charset=UTF-8
Content-Length: 19
Date: Mon, 06 May 2019 01:36:08 GMT
this is protect 111
刷新token
zuizuideMacBook-Pro:~ zuizui$ curl -i http://localhost:8080/oauth/token -d "grant_type=refresh_token&refresh_token=ebf6584f-45d7-4e14-b224-9bdb3a30d684&client_id=demoApp&client_secret=demoAppSecret"
HTTP/1.1 200
Cache-Control: no-store
Pragma: no-cache
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
X-Frame-Options: DENY
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 06 May 2019 02:04:45 GMT
{"access_token":"a13eb573-1946-4bf5-a885-f05039aaf981","token_type":"bearer","refresh_token":"49bb3cd9-6cd2-4d49-91d0-da76de72c36a","expires_in":1199,"scope":"all"}
WebSecurityConfigurerAdapter和ResourceServerConfigurerAdapter
- WebSecurityConfigurerAdapter用于保护oauth相关的endpoints,同时主要作用于用户的登录(
form login
,Basic auth
) - ResourceServerConfigurerAdapter用于保护oauth要开放的资源,同时主要作用于client端以及token的认证(
Bearer auth
)
因此二者是分工协作的
- 在WebSecurityConfigurerAdapter不拦截oauth要开放的资源
- 在ResourceServerConfigurerAdapter配置需要token验证的资源
authorization_code
在之前的基础上增加授权类型的配置
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("demoApp")
.secret(passwordEncoder.encode("demoAppSecret"))
.authorizedGrantTypes("client_credentials", "password", "refresh_token", "authorization_code")
.scopes("all")
.resourceIds("oauth2-resource")
.accessTokenValiditySeconds(1200)
.refreshTokenValiditySeconds(50000);
}
1、请求授权码
GET http://localhost:8080/oauth/authorize?response_type=code&client_id=demoApp&redirect_uri=http://baidu.com
2、在页面进行授权,跳转到redirect_uri,并带上了授权码。
3、通过授权码获取token
zuizuideMacBook-Pro:~ zuizui$ curl -i -d "grant_type=authorization_code&code=Fxa2B9&client_id=demoApp&client_secret=demoAppSecret&redirect_uri=http://baidu.com&scope=all" -X POST http://localhost:8080/oauth/token
HTTP/1.1 200
Cache-Control: no-store
Pragma: no-cache
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
X-Frame-Options: DENY
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 06 May 2019 08:44:52 GMT
{"access_token":"af929ab8-333c-4e5d-845a-2fe6a347c3ff","token_type":"bearer","refresh_token":"f186692e-8b39-444a-9554-54777bbf74fa","expires_in":1120,"scope":"all"}
4、使用token访问资源
zuizuideMacBook-Pro:~ zuizui$ curl -i -H "Authorization: bearer 833a13f0-f665-4528-8536-6d79c06a1f74" http://localhost:8080/api/protect/11111
HTTP/1.1 200
Set-Cookie: JSESSIONID=AEEBE7A26AB987143CB6E9486B1B31AB; Path=/; HttpOnly
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: text/plain;charset=UTF-8
Content-Length: 21
Date: Mon, 06 May 2019 09:04:24 GMT
this is protect 11111