├── .gitignore
├── OAuth2Client
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── example
│ │ ├── OAuth2ResourceServer.java
│ │ ├── ResourceController.java
│ │ └── config
│ │ ├── GlobalMethodSecurityConfiguration.java
│ │ ├── JwtConfiguration.java
│ │ └── OAuth2ResourceConfiguration.java
│ └── resources
│ ├── application.properties
│ ├── banner.txt
│ └── public.cert
├── OAuth2Server
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── example
│ │ ├── AuthServerApplication.java
│ │ ├── OAuth2Configuration.java
│ │ └── WebSecurityConfig.java
│ └── resources
│ ├── application.properties
│ ├── banner.txt
│ ├── jwt.jks
│ └── schema.sql
├── README.md
└── pom.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .idea
3 | *.mvn
4 | mvnw
5 | *.cmd
6 | target
--------------------------------------------------------------------------------
/OAuth2Client/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | com.example
7 | OAuth2Client
8 | 1.0
9 | jar
10 |
11 | OAuth2Client
12 | Demo project for Spring Boot
13 |
14 |
15 | org.springframework.boot
16 | spring-boot-starter-parent
17 | 1.4.1.RELEASE
18 |
19 |
20 |
21 |
22 | UTF-8
23 | UTF-8
24 | 1.8
25 |
26 |
27 |
28 |
29 | org.springframework.security.oauth
30 | spring-security-oauth2
31 |
32 |
33 | org.springframework.security
34 | spring-security-jwt
35 |
36 |
37 | org.springframework.boot
38 | spring-boot-starter-web
39 |
40 |
41 |
42 |
43 |
44 |
45 | org.springframework.cloud
46 | spring-cloud-dependencies
47 | Camden.SR1
48 | pom
49 | import
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | org.springframework.boot
58 | spring-boot-maven-plugin
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/OAuth2Client/src/main/java/com/example/OAuth2ResourceServer.java:
--------------------------------------------------------------------------------
1 | package com.example;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
6 | import org.springframework.security.oauth2.provider.OAuth2Authentication;
7 | import org.springframework.web.bind.annotation.RequestMapping;
8 | import org.springframework.web.bind.annotation.RestController;
9 |
10 | import java.security.Principal;
11 | import java.util.UUID;
12 |
13 | @SpringBootApplication
14 | public class OAuth2ResourceServer {
15 |
16 | public static void main(String[] args) {
17 | SpringApplication.run(OAuth2ResourceServer.class, args);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/OAuth2Client/src/main/java/com/example/ResourceController.java:
--------------------------------------------------------------------------------
1 | package com.example;
2 |
3 | import org.springframework.security.access.prepost.PreAuthorize;
4 | import org.springframework.web.bind.annotation.RequestMapping;
5 | import org.springframework.web.bind.annotation.RequestMethod;
6 | import org.springframework.web.bind.annotation.RestController;
7 |
8 | import java.util.UUID;
9 |
10 | @RestController
11 | @RequestMapping("/users")
12 | public class ResourceController {
13 |
14 | @RequestMapping(method = RequestMethod.GET)
15 | public String readUser() {
16 | return "read user " + UUID.randomUUID().toString();
17 | }
18 |
19 | @PreAuthorize("hasAuthority('WRITE')")
20 | @RequestMapping(method = RequestMethod.POST)
21 | public String writeUser() {
22 | return "write user " + UUID.randomUUID().toString();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/OAuth2Client/src/main/java/com/example/config/GlobalMethodSecurityConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.example.config;
2 |
3 | import org.springframework.context.annotation.Configuration;
4 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
5 |
6 | @Configuration
7 | @EnableGlobalMethodSecurity(prePostEnabled = true)
8 | public class GlobalMethodSecurityConfiguration {
9 | }
10 |
--------------------------------------------------------------------------------
/OAuth2Client/src/main/java/com/example/config/JwtConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.example.config;
2 |
3 | import org.springframework.beans.factory.annotation.Qualifier;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.Configuration;
6 | import org.springframework.core.io.ClassPathResource;
7 | import org.springframework.core.io.Resource;
8 | import org.springframework.security.oauth2.provider.token.TokenStore;
9 | import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
10 | import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
11 | import org.springframework.util.FileCopyUtils;
12 |
13 | import java.io.IOException;
14 |
15 | @Configuration
16 | public class JwtConfiguration {
17 |
18 | @Bean
19 | @Qualifier("tokenStore")
20 | public TokenStore tokenStore() {
21 | return new JwtTokenStore(jwtTokenEnhancer());
22 | }
23 |
24 | @Bean
25 | protected JwtAccessTokenConverter jwtTokenEnhancer() {
26 | JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
27 | Resource resource = new ClassPathResource("public.cert");
28 | String publicKey;
29 | try {
30 | publicKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
31 | } catch (IOException e) {
32 | throw new RuntimeException(e);
33 | }
34 | converter.setVerifierKey(publicKey);
35 | return converter;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/OAuth2Client/src/main/java/com/example/config/OAuth2ResourceConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.example.config;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.context.annotation.Configuration;
7 | import org.springframework.http.HttpMethod;
8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
9 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
10 | import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
11 | import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
12 | import org.springframework.security.oauth2.provider.token.TokenStore;
13 |
14 | @Configuration
15 | @EnableResourceServer
16 | @SuppressWarnings("SpringJavaAutowiringInspection")
17 | public class OAuth2ResourceConfiguration extends ResourceServerConfigurerAdapter {
18 |
19 | Logger log = LoggerFactory.getLogger(OAuth2ResourceConfiguration.class);
20 |
21 | @Override
22 | public void configure(HttpSecurity http) throws Exception {
23 | http
24 | .csrf().disable()
25 | .authorizeRequests()
26 | .antMatchers("/**").authenticated()
27 | .antMatchers(HttpMethod.GET, "/users").hasAuthority("READ")
28 | .antMatchers(HttpMethod.POST, "/users").hasAuthority("WRITE");
29 | }
30 |
31 |
32 | @Override
33 | public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
34 | log.info("Configuring ResourceServerSecurityConfigurer ");
35 | resources.resourceId("users").tokenStore(tokenStore);
36 | }
37 |
38 | @Autowired
39 | TokenStore tokenStore;
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/OAuth2Client/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | server.port=9090
--------------------------------------------------------------------------------
/OAuth2Client/src/main/resources/banner.txt:
--------------------------------------------------------------------------------
1 | .::::.
2 | .::::::::.
3 | :::::::::::
4 | ..:::::::::::'
5 | '::::::::::::'
6 | .::::::::::
7 | '::::::::::::::..
8 | ..::::::::::::.
9 | ``::::::::::::::::
10 | ::::``:::::::::' .:::.
11 | ::::' ':::::' .::::::::.
12 | .::::' :::: .:::::::'::::.
13 | .:::' ::::: .:::::::::' ':::::.
14 | .::' :::::.:::::::::' ':::::.
15 | .::' ::::::::::::::' ``::::.
16 | ...::: ::::::::::::' ``::.
17 | ```` ':. ':::::::::' ::::..
18 | '.:::::' ':'````..
--------------------------------------------------------------------------------
/OAuth2Client/src/main/resources/public.cert:
--------------------------------------------------------------------------------
1 | -----BEGIN PUBLIC KEY-----
2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwR84LFHwnK5GXErnwkmD
3 | mPOJl4CSTtYXCqmCtlbF+5qVOosu0YsM2DsrC9O2gun6wVFKkWYiMoBSjsNMSI3Z
4 | w5JYgh+ldHvA+MIex2QXfOZx920M1fPUiuUPgmnTFS+Z3lmK3/T6jJnmciUPY1pe
5 | h4MXL6YzeI0q4W9xNBBeKT6FDGpduc0FC3OlXHfLbVOThKmAUpAWFDwf9/uUA//l
6 | 3PLchmV6VwTcUaaHp5W8Af/GU4lPGZbTAqOxzB9ukisPFuO1DikacPhrOQgdxtqk
7 | LciRTa884uQnkFwSguOEUYf3ni8GNRJauIuW0rVXhMOs78pKvCKmo53M0tqeC6ul
8 | +QIDAQAB
9 | -----END PUBLIC KEY-----
--------------------------------------------------------------------------------
/OAuth2Server/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | com.example
7 | OAuth2Server
8 | 1.0
9 | jar
10 |
11 | OAuth2Server
12 | Demo project for Spring Boot
13 |
14 |
15 | org.springframework.boot
16 | spring-boot-starter-parent
17 | 1.4.1.RELEASE
18 |
19 |
20 |
21 |
22 | UTF-8
23 | UTF-8
24 | 1.8
25 |
26 |
27 |
28 |
29 | org.springframework.security.oauth
30 | spring-security-oauth2
31 |
32 |
33 | org.springframework.security
34 | spring-security-jwt
35 |
36 |
37 | org.springframework.boot
38 | spring-boot-starter-web
39 |
40 |
41 | com.google.guava
42 | guava
43 | 22.0
44 |
45 |
46 |
47 |
48 |
49 |
50 | org.springframework.cloud
51 | spring-cloud-dependencies
52 | Camden.SR1
53 | pom
54 | import
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | org.springframework.boot
63 | spring-boot-maven-plugin
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/OAuth2Server/src/main/java/com/example/AuthServerApplication.java:
--------------------------------------------------------------------------------
1 | package com.example;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.web.bind.annotation.RequestMapping;
6 | import org.springframework.web.bind.annotation.RestController;
7 |
8 | import java.security.Principal;
9 |
10 | @RestController
11 | @SpringBootApplication
12 | public class AuthServerApplication {
13 |
14 | @RequestMapping("/user")
15 | public Principal user(Principal user) {
16 | return user;
17 | }
18 |
19 | public static void main(String[] args) {
20 | SpringApplication.run(AuthServerApplication.class, args);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/OAuth2Server/src/main/java/com/example/OAuth2Configuration.java:
--------------------------------------------------------------------------------
1 | package com.example;
2 |
3 | import com.google.common.collect.Lists;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.beans.factory.annotation.Qualifier;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 | import org.springframework.core.io.ClassPathResource;
9 | import org.springframework.security.authentication.AuthenticationManager;
10 | import org.springframework.security.core.authority.SimpleGrantedAuthority;
11 | import org.springframework.security.core.userdetails.User;
12 | import org.springframework.security.core.userdetails.UserDetails;
13 | import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
14 | import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
15 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
16 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
17 | import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
18 | import org.springframework.security.oauth2.provider.token.TokenStore;
19 | import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
20 | import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
21 | import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
22 | import org.springframework.security.provisioning.InMemoryUserDetailsManager;
23 |
24 | import java.util.ArrayList;
25 | import java.util.List;
26 |
27 | @Configuration
28 | @EnableAuthorizationServer
29 | @EnableResourceServer
30 | @SuppressWarnings("SpringJavaAutowiringInspection")
31 | public class OAuth2Configuration extends AuthorizationServerConfigurerAdapter {
32 | @Override
33 | public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
34 | clients.inMemory()
35 | .withClient("acme") // clientId
36 | .secret("acmesecret") // clientSecret
37 | .scopes("openid")
38 | .autoApprove(true)
39 | .authorities("READ", "WRITE")
40 | .authorizedGrantTypes("implicit", "refresh_token", "password", "authorization_code", "client_credentials");
41 | }
42 |
43 | @Override
44 | public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
45 | List userDetailsList = new ArrayList<>();
46 | userDetailsList.add(new User("user","user", Lists.newArrayList(new SimpleGrantedAuthority("READ"))));
47 | userDetailsList.add(new User("admin","admin",Lists.newArrayList(new SimpleGrantedAuthority("READ"),new SimpleGrantedAuthority("WRITE"))));
48 |
49 | endpoints.tokenStore(tokenStore())
50 | .tokenEnhancer(jwtTokenEnhancer())
51 | .authenticationManager(authenticationManager)
52 | // 需要指定userDetailsService否则在请求refresh_token接口时报userDetailsService is required
53 | .userDetailsService(new InMemoryUserDetailsManager(userDetailsList));
54 | }
55 |
56 | @Autowired
57 | @Qualifier("authenticationManagerBean")
58 | private AuthenticationManager authenticationManager;
59 |
60 | @Bean
61 | public TokenStore tokenStore() {
62 | return new JwtTokenStore(jwtTokenEnhancer());
63 | }
64 |
65 | @Bean
66 | protected JwtAccessTokenConverter jwtTokenEnhancer() {
67 | // mySecretKey为产生秘钥文件jwt.jks时所使用的密码
68 | KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "mySecretKey".toCharArray());
69 | JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
70 | converter.setKeyPair(keyStoreKeyFactory.getKeyPair("jwt"));
71 | return converter;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/OAuth2Server/src/main/java/com/example/WebSecurityConfig.java:
--------------------------------------------------------------------------------
1 | package com.example;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.security.authentication.AuthenticationManager;
6 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
7 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
8 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
9 |
10 | @Configuration
11 | class WebSecurityConfig extends WebSecurityConfigurerAdapter {
12 |
13 | @Override
14 | @Bean
15 | public AuthenticationManager authenticationManagerBean() throws Exception {
16 | return super.authenticationManagerBean();
17 | }
18 |
19 | @Override
20 | protected void configure(HttpSecurity http) throws Exception {
21 | http.authorizeRequests()
22 | .anyRequest().authenticated()
23 | .and()
24 | //.formLogin().and() // 基于Form表单的认证,用户可自定义
25 | .httpBasic(); // 启用HTTPBasic认证
26 | }
27 |
28 | @Override
29 | protected void configure(AuthenticationManagerBuilder auth) throws Exception {
30 | // auth.userDetailsService(); 可指定从数据库加载用户信息
31 | auth.inMemoryAuthentication()
32 | .withUser("user")
33 | .password("user")
34 | .authorities("READ")
35 | .and()
36 | .withUser("admin")
37 | .password("admin")
38 | .authorities("READ", "WRITE");
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/OAuth2Server/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | debug=true
2 | server.port=9999
--------------------------------------------------------------------------------
/OAuth2Server/src/main/resources/banner.txt:
--------------------------------------------------------------------------------
1 | .::::.
2 | .::::::::.
3 | :::::::::::
4 | ..:::::::::::'
5 | '::::::::::::'
6 | .::::::::::
7 | '::::::::::::::..
8 | ..::::::::::::.
9 | ``::::::::::::::::
10 | ::::``:::::::::' .:::.
11 | ::::' ':::::' .::::::::.
12 | .::::' :::: .:::::::'::::.
13 | .:::' ::::: .:::::::::' ':::::.
14 | .::' :::::.:::::::::' ':::::.
15 | .::' ::::::::::::::' ``::::.
16 | ...::: ::::::::::::' ``::.
17 | ```` ':. ':::::::::' ::::..
18 | '.:::::' ':'````..
--------------------------------------------------------------------------------
/OAuth2Server/src/main/resources/jwt.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/v5tech/spring-boot-oauth2-example/2a8d6efa2152d631f89b1522bb5be64efd6d8301/OAuth2Server/src/main/resources/jwt.jks
--------------------------------------------------------------------------------
/OAuth2Server/src/main/resources/schema.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS users;
2 | CREATE TABLE users (
3 | username VARCHAR(50) NOT NULL PRIMARY KEY,
4 | password VARCHAR(50) NOT NULL,
5 | enabled BOOLEAN NOT NULL
6 | );
7 |
8 | DROP TABLE IF EXISTS authorities;
9 | CREATE TABLE authorities (
10 | username VARCHAR(50) NOT NULL,
11 | authority VARCHAR(50) NOT NULL,
12 | CONSTRAINT fk_authorities_users FOREIGN KEY (username) REFERENCES users (username)
13 | );
14 | CREATE UNIQUE INDEX ix_auth_username
15 | ON authorities (username, authority);
16 |
17 | DROP TABLE IF EXISTS oauth_client_details;
18 | CREATE TABLE oauth_client_details (
19 | client_id VARCHAR(255) PRIMARY KEY,
20 | resource_ids VARCHAR(255),
21 | client_secret VARCHAR(255),
22 | scope VARCHAR(255),
23 | authorized_grant_types VARCHAR(255),
24 | web_server_redirect_uri VARCHAR(255),
25 | authorities VARCHAR(255),
26 | access_token_validity INTEGER,
27 | refresh_token_validity INTEGER,
28 | additional_information VARCHAR(4096),
29 | autoapprove VARCHAR(255)
30 | );
31 |
32 | DROP TABLE IF EXISTS oauth_client_token;
33 | CREATE TABLE oauth_client_token (
34 | token_id VARCHAR(255),
35 | token VARCHAR(255),
36 | authentication_id VARCHAR(255) PRIMARY KEY,
37 | user_name VARCHAR(255),
38 | client_id VARCHAR(255)
39 | );
40 |
41 | DROP TABLE IF EXISTS oauth_access_token;
42 | CREATE TABLE oauth_access_token (
43 | token_id VARCHAR(255),
44 | token VARCHAR(255),
45 | authentication_id VARCHAR(255) PRIMARY KEY,
46 | user_name VARCHAR(255),
47 | client_id VARCHAR(255),
48 | authentication VARCHAR(255),
49 | refresh_token VARCHAR(255)
50 | );
51 |
52 | DROP TABLE IF EXISTS oauth_refresh_token;
53 | CREATE TABLE oauth_refresh_token (
54 | token_id VARCHAR(255),
55 | token VARCHAR(255),
56 | authentication VARCHAR(255)
57 | );
58 |
59 | DROP TABLE IF EXISTS oauth_code;
60 | CREATE TABLE oauth_code (
61 | code VARCHAR(255),
62 | authentication VARCHAR(255)
63 | );
64 |
65 | DROP TABLE IF EXISTS oauth_approvals;
66 | CREATE TABLE oauth_approvals (
67 | userId VARCHAR(255),
68 | clientId VARCHAR(255),
69 | scope VARCHAR(255),
70 | status VARCHAR(10),
71 | expiresAt TIMESTAMP,
72 | lastModifiedAt TIMESTAMP
73 | );
74 |
75 | -- customized oauth_client_details table
76 | DROP TABLE IF EXISTS custom_oauth_client_details;
77 | CREATE TABLE custom_oauth_client_details (
78 | appId VARCHAR(255) PRIMARY KEY,
79 | resourceIds VARCHAR(255),
80 | appSecret VARCHAR(255),
81 | scope VARCHAR(255),
82 | grantTypes VARCHAR(255),
83 | redirectUrl VARCHAR(255),
84 | authorities VARCHAR(255),
85 | access_token_validity INTEGER,
86 | refresh_token_validity INTEGER,
87 | additionalInformation VARCHAR(4096),
88 | autoApproveScopes VARCHAR(255)
89 | );
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # spring-boot-oauth2-example
2 |
3 | ### OAuth2 Server端配置
4 |
5 | 生成JWT秘钥
6 |
7 | ```
8 | $ keytool -genkeypair -alias jwt -keyalg RSA -dname "CN=jwt, L=Berlin, S=Berlin, C=DE" -keypass mySecretKey -keystore jwt.jks -storepass mySecretKey
9 | ```
10 |
11 | 将秘钥存储到`OAuth2 Server`端的`src/main/resources/jwt.jks`
12 |
13 | 根据秘钥生成公钥
14 |
15 | ```
16 | $ keytool -list -rfc --keystore jwt.jks | openssl x509 -inform pem -pubkey
17 | ▒▒▒▒▒▒Կ▒▒▒▒▒: mySecretKey
18 | -----BEGIN PUBLIC KEY-----
19 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoLnfiQCCqhXbrRHg/hhR
20 | RIBffp/B/c5xyXqKJ3QYKU2s3iPo9eVFNvZu80KzKhQ6CsTgzRHfujxVp3IOB/CN
21 | tKPfx2P6ulIS0R9sA4mDiXINYLao8Kpg7uK865QehYitB5voMNDTzi3sjUBoKlK5
22 | ps46Pd8YmuXmM7TxonFGYjaGGtdt+w0RiC5ggF3mvzk6AHUR1KupCNPpcsGNMYGG
23 | ek4FMcxZf2QBJEtvRN76blwQiUDX6R7xx4yeKsew2sVU86hE14h2NbuvVtdDIOKM
24 | +F76o3+zGQzn4/Ijcs9faWoHbLUmigEmYU08B2zc3/6eDiaFsa0Lcm8QCWprVfpe
25 | DQIDAQAB
26 | -----END PUBLIC KEY-----
27 | -----BEGIN CERTIFICATE-----
28 | MIIDGTCCAgGgAwIBAgIEULSkdTANBgkqhkiG9w0BAQsFADA9MQswCQYDVQQGEwJE
29 | RTEPMA0GA1UECBMGQmVybGluMQ8wDQYDVQQHEwZCZXJsaW4xDDAKBgNVBAMTA2p3
30 | dDAeFw0xNjExMDUwNjAyNTFaFw0xNzAyMDMwNjAyNTFaMD0xCzAJBgNVBAYTAkRF
31 | MQ8wDQYDVQQIEwZCZXJsaW4xDzANBgNVBAcTBkJlcmxpbjEMMAoGA1UEAxMDand0
32 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoLnfiQCCqhXbrRHg/hhR
33 | RIBffp/B/c5xyXqKJ3QYKU2s3iPo9eVFNvZu80KzKhQ6CsTgzRHfujxVp3IOB/CN
34 | tKPfx2P6ulIS0R9sA4mDiXINYLao8Kpg7uK865QehYitB5voMNDTzi3sjUBoKlK5
35 | ps46Pd8YmuXmM7TxonFGYjaGGtdt+w0RiC5ggF3mvzk6AHUR1KupCNPpcsGNMYGG
36 | ek4FMcxZf2QBJEtvRN76blwQiUDX6R7xx4yeKsew2sVU86hE14h2NbuvVtdDIOKM
37 | +F76o3+zGQzn4/Ijcs9faWoHbLUmigEmYU08B2zc3/6eDiaFsa0Lcm8QCWprVfpe
38 | DQIDAQABoyEwHzAdBgNVHQ4EFgQUaisB+TYSddByoWa9a9Xhpjx3+YQwDQYJKoZI
39 | hvcNAQELBQADggEBAHyLIstXg+O63ETlsyovscyGVv4F1MrWx3nmZT++mlr7Ivbw
40 | UoOzG71knOkaAINox/BrPCciDddBIRkKDdT6orolMg1HiPZGwCt+DJ2c7J/kFyJ3
41 | kCUeQp6JefHIKrQUeEErPSDaQpm+afc0mq5I/FP5Kg0aSg6sUr1SJfqG6aEWf/pg
42 | 8V8I3/bGFG1QzHER3R/hX2f+09UElgIvIK8KTJoT1EnVRbzDyG0IEvvheIk/TXG+
43 | ICaxFrDCkavbP2Swx7HuMNi9FQEIQ7lwPYtzX6cfeYYNHJ1CR70uN9YYkroTRWLx
44 | 4vsgVFhzRbGvnW5Ufv18lI0IThHsU395F/75aFw=
45 | -----END CERTIFICATE-----
46 | ```
47 |
48 | 注:mySecretKey为生成秘钥时使用的密码
49 |
50 | ### OAuth2 Client端配置
51 |
52 | 拷贝生成的公钥存储到`OAuth2 Client`端`src/main/resources/public.cert`
53 |
54 | ```
55 | -----BEGIN PUBLIC KEY-----
56 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoLnfiQCCqhXbrRHg/hhR
57 | RIBffp/B/c5xyXqKJ3QYKU2s3iPo9eVFNvZu80KzKhQ6CsTgzRHfujxVp3IOB/CN
58 | tKPfx2P6ulIS0R9sA4mDiXINYLao8Kpg7uK865QehYitB5voMNDTzi3sjUBoKlK5
59 | ps46Pd8YmuXmM7TxonFGYjaGGtdt+w0RiC5ggF3mvzk6AHUR1KupCNPpcsGNMYGG
60 | ek4FMcxZf2QBJEtvRN76blwQiUDX6R7xx4yeKsew2sVU86hE14h2NbuvVtdDIOKM
61 | +F76o3+zGQzn4/Ijcs9faWoHbLUmigEmYU08B2zc3/6eDiaFsa0Lcm8QCWprVfpe
62 | DQIDAQAB
63 | -----END PUBLIC KEY-----
64 | ```
65 |
66 | ### 运行OAuth2 Server,获取accesstoken
67 |
68 | #### 1、password方式(密码凭证许可)
69 |
70 | 使用grant_type=password,使用用户名密码直接授权获取`accesstoken`
71 |
72 | * 获取具有只读权限的`accesstoken`
73 |
74 | ```
75 | $ curl "acme:acmesecret@localhost:9999/oauth/token" -d "grant_type=password&username=user&password=user"
76 | {"access_token":"eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0NzgzNzQ3NTcsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJSRUFEIl0sImp0aSI6Ijg4Zjk4ZWYxLTZjOGItNDA1MC1iOTc3LWFlYjcxMzhlNjg2OCIsImNsaWVudF9pZCI6ImFwcENsaWVudCIsInNjb3BlIjpbIk9BdXRoMiJdfQ.sop6d8acs6piMgiN1FH8EVw4Qglh69wU5vvdMQZ87YSVjtaTCQqpf4kR65jXtqTNuTTZ8azf_aD5GoIBaqVrDDGEHk8dZLciobgD1vexpX2XnrfAFUt0xHg1LXIO_mJtf7x4CBiF4ysGWdlhWQbX2wq5YNvG3QhIkRHdnvxBNSiLJPSaa2sqHKxdXs4J7tLnNN415K1TI2pV7_6C3p-BQD2qfdIWZXB2JLYSmTVjnvUQtjIvLDNu8OL7mvyfP2F_d-b-PAPYU4ul9RZfnB9hYr02i8M-7lYh7pK-SoA0MlMlIP-QSDpACTqWuWpyP_q8N-tFDwPZ997ES2FOaWArFA","token_type":"bearer","refresh_token":"eyJhbGciOiJSUzI1NiJ9.eyJ1c2VyX25hbWUiOiJ1c2VyIiwic2NvcGUiOlsiT0F1dGgyIl0sImF0aSI6Ijg4Zjk4ZWYxLTZjOGItNDA1MC1iOTc3LWFlYjcxMzhlNjg2OCIsImV4cCI6MTQ4MDkyMzU1NywiYXV0aG9yaXRpZXMiOlsiUkVBRCJdLCJqdGkiOiJmMWNhYTI5YS05Zjg5LTQ4YmItYTIxZi02MTI3OTM0YzI1MWEiLCJjbGllbnRfaWQiOiJhcHBDbGllbnQifQ.ZevWVRKmRIqJ7njhXmcykgGJ4sAY9qItMdFiLDEX68hAYcRpiv2Q5mifIH3HPvVB-Nv84oGHI2cam2Bs4gIH6SJbBlzYxanOdWOkyN9vknVEkFiA0bwh9hyem94sbbTmxYr3kFfuhFvPma6v7iD-YFxnVqvBo8WoMEMEwZlcOxD5fV0yyRdst_i7PTfPc4hyc5sfW75IJdGduD_tp5ePnRMO6pXUYXhfcCXgf3zHYd_HlR88cqA__yh_0b3AY4TFts19SW68LgzG72UOP5F1HYzLetiwtAwpnPJawTDCYQLMf5YCnHoNxZR3RKcuDE8xwgt_Ytn8Tg9KHMXZzk0Gig","expires_in":43199,"scope":"OAuth2","jti":"88f98ef1-6c8b-4050-b977-aeb7138e6868"}
77 | ```
78 |
79 | * 获取具有读写权限的`accesstoken`
80 |
81 | ```
82 | $ curl "acme:acmesecret@localhost:9999/oauth/token" -d "grant_type=password&username=admin&password=admin"
83 | {"access_token":"eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0NzgzNzQ4MDQsInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiUkVBRCIsIldSSVRFIl0sImp0aSI6IjgwYTU3Y2NkLTQzMGItNGQ5Zi1iODRjLWE0YTNjODQwODAxZiIsImNsaWVudF9pZCI6ImFwcENsaWVudCIsInNjb3BlIjpbIk9BdXRoMiJdfQ.nePNHgpMDukjhyZEYe_PFNSoGtftA61UmPy93P3r6VCrofhQpXP6vRUCBVTY0NfPXAd250x-jrkDS3SjnrSsatMj3WoJpBjPP56SzPQu1J3gUUgnbSu2zfeFZTcA4cEyyaGvZf3tVzCH-g_DQjcPoBL-vBB-RuzML7PfY1Hj8Bbl3ODxnQzUNnvA2FPI_mDuTqiR8L9r1p6xDt9lBKWc_HiZIE7LGt8xNCUKBbj0kQVO3J_L3vUtjWVt2kuvmSJ9eEsO5U6SmZx0Z6B5Xbp5HNgB2lhBlBDZZHk6niA6Mjk1_0LQBERTNgNrtja7ukqH9gcw_s5d0mo6bJCQlzhOMg","token_type":"bearer","refresh_token":"eyJhbGciOiJSUzI1NiJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbIk9BdXRoMiJdLCJhdGkiOiI4MGE1N2NjZC00MzBiLTRkOWYtYjg0Yy1hNGEzYzg0MDgwMWYiLCJleHAiOjE0ODA5MjM2MDQsImF1dGhvcml0aWVzIjpbIlJFQUQiLCJXUklURSJdLCJqdGkiOiIyODYyZDI3ZS0xYTJiLTQ5ZjAtOGYxZC1mYjI4N2M0MGI5OTMiLCJjbGllbnRfaWQiOiJhcHBDbGllbnQifQ.AHQUjBGynSJ3YvbIIeN92vdCwpOl-dg_m_M9ju935XcPT5AwXP6l99Yo0GnhT2UNqOEV4ibygXjOSY50ZtAVc3eeQC_bGEdqV1xF4fPx8cZ60cEV-dazm69dodGKTP93uFChn5zgxmZoKQHgrxVb3dnJnzMqkDAff9lf7aEbLsI12er9RWJ5bYLJKAUo9oKA3R2m6rO4YUwWsNctXeeq545MDuMR4SXBNwZ9KPGnOn_URKtXIYShnirVJwvlHZ5ZeaaQbn-PDqVyTG4seA7Qk_UQfSany1gSVQFZbgRZjX9pzIw6LV9eRm75gzPmcHMRcS-dtG2JBBYk75R7hmBCiw","expires_in":43199,"scope":"OAuth2","jti":"80a57ccd-430b-4d9f-b84c-a4a3c840801f"}
84 | ```
85 |
86 | #### 2、refresh_token方式
87 |
88 | 使用`password方式`和`authorization_code方式`获取`accesstoken`的同时,也返回了一个`refresh_token`,使用接口`/oauth/token -d grant_type=refresh_token -d refresh_token=$refresh_token`返回一个新的`accesstoken`
89 |
90 | * password方式
91 |
92 | ```
93 | curl "acme:acmesecret@localhost:9999/oauth/token" -d "grant_type=password&username=user&password=user"
94 | {"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTYyNjI4NjgsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJSRUFEIl0sImp0aSI6IjQ3NTYwMTc3LWVhODMtNGY3OS04Y2E3LWViMjBhNjAzY2VmZSIsImNsaWVudF9pZCI6ImFjbWUiLCJzY29wZSI6WyJvcGVuaWQiXX0.LUnyJD5vp3pAcvDRh4QUgj6O6ggkljgm7wGYy_DN2n3p48DQQo6weKeGwghUplr0En4d8_Dw0w7owPU0XgNUIas6gBLfOvWp3ATe8Ie83xsnTlP5OAFVL5fhuoqeT55JlGB8RdpjXGkdGufLm1cNA8nkwuADLA0tGdhSzIN83R5KpByIt0tTuvtGdiix6YiMiAmuUlq9sC28ERq4_urbGrwQjuuMxdpZIp_Gvw7xOOYHtdKwdxqFiBhnIXnGFUZPR6nnHguGAiQ-B9ZIUYjTzCr-0Q7kMhDdY_IPKh0IvYrIx0o3occ0QRcHTe5d5OJOc-vT-qi15E0_SMToFHZdVg","token_type":"bearer","refresh_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ1c2VyIiwic2NvcGUiOlsib3BlbmlkIl0sImF0aSI6IjQ3NTYwMTc3LWVhODMtNGY3OS04Y2E3LWViMjBhNjAzY2VmZSIsImV4cCI6MTQ5ODgxMTY2OCwiYXV0aG9yaXRpZXMiOlsiUkVBRCJdLCJqdGkiOiI5MzExOTZjOS03NGU2LTQ3ZDUtYjA3MC0xMGNhYzg2NjUzZTAiLCJjbGllbnRfaWQiOiJhY21lIn0.aQqsdkQfCy4L4FjV6TZyRehG6ZjgmuWe-30uuco-g5GKmuxJGEoY6dYOiDhsTOgKoxDXJwN6kACZMePInP4cXrExK6N7n25MbUXn-IxPGb1TTy3YmcaVwLM-oP2IW-dxzYLjmT3XYgA8Xt4vY2z8trzkvR2jefP-cHP0uQq7jFjDVYSq37epJozPNYWd_630jWokjbOQr3MwMcsLjmUz8eUmMm8I7Ln1XM2RFlmw09_eFLk0FNtwuWd-IFXsjPc_2jaNASJYEfurNlTN8OH7LKe34enDZoPgpbsMxUUsF3EW9iq2NwnV9btEPqe-a-wJDCftXvV7GxmOKxdg--X2zw","expires_in":43199,"scope":"openid","jti":"47560177-ea83-4f79-8ca7-eb20a603cefe"}
95 | ```
96 | 上面的请求返回一个`access_token`和`refresh_token`,使用该`refresh_token`请求`/oauth/token -d grant_type=refresh_token -d refresh_token`接口返回一个新的`accesstoken`
97 |
98 | ```
99 | curl acme:acmesecret@localhost:9999/oauth/token -d grant_type=refresh_token -d refresh_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ1c2VyIiwic2NvcGUiOlsib3BlbmlkIl0sImF0aSI6IjQ3NTYwMTc3LWVhODMtNGY3OS04Y2E3LWViMjBhNjAzY2VmZSIsImV4cCI6MTQ5ODgxMTY2OCwiYXV0aG9yaXRpZXMiOlsiUkVBRCJdLCJqdGkiOiI5MzExOTZjOS03NGU2LTQ3ZDUtYjA3MC0xMGNhYzg2NjUzZTAiLCJjbGllbnRfaWQiOiJhY21lIn0.aQqsdkQfCy4L4FjV6TZyRehG6ZjgmuWe-30uuco-g5GKmuxJGEoY6dYOiDhsTOgKoxDXJwN6kACZMePInP4cXrExK6N7n25MbUXn-IxPGb1TTy3YmcaVwLM-oP2IW-dxzYLjmT3XYgA8Xt4vY2z8trzkvR2jefP-cHP0uQq7jFjDVYSq37epJozPNYWd_630jWokjbOQr3MwMcsLjmUz8eUmMm8I7Ln1XM2RFlmw09_eFLk0FNtwuWd-IFXsjPc_2jaNASJYEfurNlTN8OH7LKe34enDZoPgpbsMxUUsF3EW9iq2NwnV9btEPqe-a-wJDCftXvV7GxmOKxdg--X2zw
100 | {"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTYyNjI5MTcsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJSRUFEIl0sImp0aSI6IjRhZjk1NjY1LTNiOTctNDc2Yy04MjMzLWEyMjJhZDYxNDg1OCIsImNsaWVudF9pZCI6ImFjbWUiLCJzY29wZSI6WyJvcGVuaWQiXX0.GfEYwBZ1cBloU-w_vV7xYqoVI0u_BnfF1AyLb9YvpJFcOZWf0go8GdJ-ZAftUd5fvK8iocBh-Jg0cCkq7q1hySLu9T-DWv6Z80QeTl2e56wNy7W-qGx66cdpdvNUFnJnhsNZIjbnJH67_ccerohjHuM9HlTCK9O58sUmnPYPZwQIhH5dsR_uoXVHQhdXm-z7selPozvTtM6arGeZqhvHs0TK2X7WCFSvtWyynNKSrdrnw2QKNYHdZJSFyh7dtehinxtpiBEvARnfFj0OigBoOQTFmlgzisGOjuyP3ndcg4lcTnyy10LYzALfBm8Ou5Rrs9rxhHFuXIBks7-Db7d7Dw","token_type":"bearer","refresh_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ1c2VyIiwic2NvcGUiOlsib3BlbmlkIl0sImF0aSI6IjRhZjk1NjY1LTNiOTctNDc2Yy04MjMzLWEyMjJhZDYxNDg1OCIsImV4cCI6MTQ5ODgxMTY2OCwiYXV0aG9yaXRpZXMiOlsiUkVBRCJdLCJqdGkiOiI5MzExOTZjOS03NGU2LTQ3ZDUtYjA3MC0xMGNhYzg2NjUzZTAiLCJjbGllbnRfaWQiOiJhY21lIn0.Y3JQppvnMs2vFZAIJcz9DHrz3T-pgfLG2lb7zfaumu0Jhblv84-GUrTC7w2qi2L_Hw66SuQ0TsHH0jGE8V_B3SmsaLSYIH4BQLBq2aUrjlWU7mi8QXWV_hfHWBMRX49QtsqRm65no7nRkOEvEgL_rRX3U2mwzp8pR7366Bvcl1k7mWEQqHKIEARCyi77Azjm4ExL5Ktu6CHLhsNrqAxYxjGpHdTVMxe3SJ-9JvynIywA6tgFlHvbbaI7kv7mLNx2jEqlipoJ0YRkGgCxLn3lce6Aq0E5Ms8FKJm-ehZAeAcpr2f1ulKa67bCNfhahluQ4VRWEQL3Q7xmNkT7cDzPfg","expires_in":43199,"scope":"openid","jti":"4af95665-3b97-476c-8233-a222ad614858"}
101 | ```
102 |
103 | * authorization_code方式
104 |
105 | 浏览器请求`http://localhost:9999/oauth/authorize?response_type=code&client_id=acme&redirect_uri=http://notes.coding.me`将被重定向到`http://notes.coding.me/?code=Axjz0f`
106 |
107 | 使用该`code`值请求`/oauth/token -d grant_type=authorization_code`接口或取`accesstoken`
108 |
109 | ```
110 | curl acme:acmesecret@localhost:9999/oauth/token -d grant_type=authorization_code -d client_id=acme -d redirect_uri=http://notes.coding.me -d code=Axjz0f
111 | {"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTYyNjMzMzAsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJSRUFEIl0sImp0aSI6ImFhZTgzOTYxLTlmZjAtNGM4NC04YzA2LWM0NzE5YjI2YmFiOCIsImNsaWVudF9pZCI6ImFjbWUiLCJzY29wZSI6WyJvcGVuaWQiXX0.ZWYqGtPr5OdXyZ1mV2IX887l41Z4z74Tf4gY8aj7ZMeRYdwsHihKOFqhmUr0rCV1ihbvSu0p6E7O9vIH53m_a-LiAa4EAcpEVh7r9_yHzHln5RXBA2o09TySe1NgU_TjhbamgTK-IZdtdNRmxC18pW_9Xs-h2rV3LztmVtFOoNSao2fnyeZ9mEC6GIdjm7mqxnBB4S5VUVbrLU6DnnL2zqZACRUBpwNbe4g1dCept-cITuxYoaAjKbBq9ogUStiAgnScTHqlZ7UdtUVS1KKBTfKcF6XT6kwF5d5VuS1nehvxglXuP_WPJZx5b0-m53Avw_sH_iSeYeNd_kae452v6g","token_type":"bearer","refresh_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ1c2VyIiwic2NvcGUiOlsib3BlbmlkIl0sImF0aSI6ImFhZTgzOTYxLTlmZjAtNGM4NC04YzA2LWM0NzE5YjI2YmFiOCIsImV4cCI6MTQ5ODgxMjEzMCwiYXV0aG9yaXRpZXMiOlsiUkVBRCJdLCJqdGkiOiIyMTMxZTRjZS0wY2VmLTQ5MzMtODc4Ni0wMjhmY2ZiYWRjOWQiLCJjbGllbnRfaWQiOiJhY21lIn0.V_780DB7hlAo6cDr3d6ffNhv1acSR4xzJKdx3wnv-Tms-1Q4ckgSPspHmTd4k9R3vKRw22ywplr20oX3ZxlMNEs3Qn38esg2A_PT2mxW_xHgtmyOmRLul-RLe_QuBMf3UziCp5qZGY1_Pj16HyK56rjJvswWw6j79MQToGte7G_3sgzw9HuRUcQnW-VTUDjfWt7V3DyEpQL89ST7DvL40ptzu19_ZvVaxFx40htDTj4GO8pLS3wnh8QMOBFEQcvaHJfM0yHiPm23oQcsLGV4K5FBTi80PzlGty6juONxJ-RIPBkfSzbLi4BmPuEgsZQ62YdvjKEOE3Q4DCEflSbIuQ","expires_in":43199,"scope":"openid","jti":"aae83961-9ff0-4c84-8c06-c4719b26bab8"}
112 | ```
113 |
114 | 接着使用`/oauth/token -d grant_type=refresh_token -d refresh_token=$refresh_token`接口返回一个新的`accesstoken`
115 |
116 | ```
117 | curl acme:acmesecret@localhost:9999/oauth/token -d grant_type=refresh_token -d refresh_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ1c2VyIiwic2NvcGUiOlsib3BlbmlkIl0sImF0aSI6ImFhZTgzOTYxLTlmZjAtNGM4NC04YzA2LWM0NzE5YjI2YmFiOCIsImV4cCI6MTQ5ODgxMjEzMCwiYXV0aG9yaXRpZXMiOlsiUkVBRCJdLCJqdGkiOiIyMTMxZTRjZS0wY2VmLTQ5MzMtODc4Ni0wMjhmY2ZiYWRjOWQiLCJjbGllbnRfaWQiOiJhY21lIn0.V_780DB7hlAo6cDr3d6ffNhv1acSR4xzJKdx3wnv-Tms-1Q4ckgSPspHmTd4k9R3vKRw22ywplr20oX3ZxlMNEs3Qn38esg2A_PT2mxW_xHgtmyOmRLul-RLe_QuBMf3UziCp5qZGY1_Pj16HyK56rjJvswWw6j79MQToGte7G_3sgzw9HuRUcQnW-VTUDjfWt7V3DyEpQL89ST7DvL40ptzu19_ZvVaxFx40htDTj4GO8pLS3wnh8QMOBFEQcvaHJfM0yHiPm23oQcsLGV4K5FBTi80PzlGty6juONxJ-RIPBkfSzbLi4BmPuEgsZQ62YdvjKEOE3Q4DCEflSbIuQ
118 | {"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTYyNjMzNjYsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJSRUFEIl0sImp0aSI6IjY5Y2M4NGQzLTk3NmQtNGEwYS05OTYxLTIzMjJkMzg5YTBhNCIsImNsaWVudF9pZCI6ImFjbWUiLCJzY29wZSI6WyJvcGVuaWQiXX0.BK20cQmpWVrClPzAiYLOWnxWPbGV9r8VAKab_dfTUvwO7tqAw6At4uR0FeYXuZ2TWYlJq2nwDVXNdsXEyU0PTriicMpszsOUmWUla7Vhl4HD36ymA93rO7LbIXKaZ5iiCrVNa8ybzPsN2nYhoTSesjBsWZUd9evdUAqalzcW_sa-utq4tevTLpnsiYezZwnLhI-H8f7amZ1CFA9vd_8VI-CLXxXds2qX6SQ5VRSUYbYkPdETsD_ZW1teOOTVWCBRF9-DgRSs3VVzIlArfR-Xt2sSYR4y8GNYAdGxSbE5TSnhRUMvhGmgqOUfvnWp_Bkip3Fo8ngzML8x3yqHad7DGA","token_type":"bearer","refresh_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ1c2VyIiwic2NvcGUiOlsib3BlbmlkIl0sImF0aSI6IjY5Y2M4NGQzLTk3NmQtNGEwYS05OTYxLTIzMjJkMzg5YTBhNCIsImV4cCI6MTQ5ODgxMjEzMCwiYXV0aG9yaXRpZXMiOlsiUkVBRCJdLCJqdGkiOiIyMTMxZTRjZS0wY2VmLTQ5MzMtODc4Ni0wMjhmY2ZiYWRjOWQiLCJjbGllbnRfaWQiOiJhY21lIn0.G4pjGKrpjm09yvX-6HuNvqtOUsx3sNP-0mDXx4hh_As7n0yLwqBR6EttM5fXeGa5-YFc1PiuG6AOY0AhEGfJHeeX17Nk5dG-Tjf3CikALUqfP2ywTgl3cGa9RMnJjAFZM_VJm2-G7VB5hA899jQ5KrxNqMUC_Yt7udeJxuY-iFspB7aIf07BwktiKFAkStdXmzSI07RL0cLj6Zk5Sbr_uGGka0RsXWBh-EaxqJuuXSdsWPYakgxrlKRIWEsUmg39k3f2RQ1DdJLp6ZSogPWzJmsRKU7Fpygsn46q3WzBK5gBLJzqcqK6I-qjRUqy-V7pFhCR996fC2bs3beA_6jlOQ","expires_in":43199,"scope":"openid","jti":"69cc84d3-976d-4a0a-9961-2322d389a0a4"}
119 | ```
120 |
121 | #### 3、client_credentials方式(客户端凭证许可)
122 |
123 | 使用grant_type=client_credentials,不需要用户名密码,可直接获取accesstoken
124 |
125 | ```
126 | curl acme:acmesecret@localhost:9999/oauth/token -d grant_type=client_credentials -d scope=openid
127 | {"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJvcGVuaWQiXSwiZXhwIjoxNDk2MjU3ODczLCJhdXRob3JpdGllcyI6WyJSRUFEIiwiV1JJVEUiXSwianRpIjoiMjM1YmE5MTEtOWU5NC00OTIzLWJlNjYtNjEzYzNlMTU5N2NlIiwiY2xpZW50X2lkIjoiYWNtZSJ9.v7JY7GVIbuwgcHSB88DSfP8HmeT89Pf_pdbhZ1lsaOMcLlMJBZmXSpUAr919mewh45dIad_6lMTa8PmbYFBPFmFoVQog7-3kaKEm-NbU3S09MXoLQt3Sg-Co9sEXcnZZ32SrAjTrti0MIUL_DatyfNmrTBP-ivhNUM3v9RZhsBMSCf_zfvkGdS2kiVY8oV1elxkGeQIc__-FMZtixutomuTeYlAxcG0nSEhCNgH0-V4u5GzULqOBlZA0OQbZ6ouZKB88OIm85Yp9p3jhzPDS29unkSDSZyPZZgq2lN6tGKN1Bq5LNHKi-Lr1W7XK1inCOj-kepejSr2SkHvrOKHY3w","token_type":"bearer","expires_in":43199,"scope":"openid","jti":"235ba911-9e94-4923-be66-613c3e1597ce"}
128 | ```
129 |
130 | #### 4、authorization_code方式(授权码许可)
131 |
132 | 使用`authorization_code`方式获取`accesstoken`分两步:
133 |
134 | 1、首先使用`/oauth/authorize?response_type=code`接口获取`code`
135 |
136 | 2、接着使用`/oauth/token -d grant_type=authorization_code`接口获取`accesstoken`
137 |
138 | ```
139 | http://localhost:9999/oauth/authorize?response_type=code&client_id=acme&redirect_uri=http://notes.coding.me
140 | ```
141 |
142 | 输入用户名、密码,最终重定向到`http://notes.coding.me/?code=8JsYwV`,再根据返回的`code`获取accesstoken
143 |
144 | ```
145 | $ curl acme:acmesecret@localhost:9999/oauth/token -d grant_type=authorization_code -d client_id=acme -d redirect_uri=http://notes.coding.me -d code=8JsYwV
146 | {"access_token":"eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0NzgzNzkzMzQsInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiUkVBRCIsIldSSVRFIl0sImp0aSI6IjAyMjgyMzNkLWVjY2MtNDU5YS1hYjBkLTg4ZGU2MDQ0MWIwMCIsImNsaWVudF9pZCI6ImFwcENsaWVudCIsInNjb3BlIjpbIm9wZW5pZCJdfQ.tGQjNR_Xg_E52xFEIashqWPnH-hnvBsqkKjbNiQgHUGE-4wHNJ-wxxia-cfvfD4IR5fA3AekSUWzGk3uLaD4q66HmCHgl1zdSjgXEqsEC-C1VqI_FLq0HlunFtJ6i4mHjY0nIxsIx5hhdoyVONJk3HyckegCaVUKy8g1q8hn7qiuBpcZCTUhfuYq5Lb1A3nbCUMQRB72eZ3slC8l3Y60kAhVP3gcY5gLYwPrL-1O2FuwEOO_vpoe-yzdp-WxUabpX7v-78oxkpA2LKxQu-9fNwlJmsTTzk4bZ-onRHdVtHjZr_QvqekXTqDBnXVrv26r2cpSqli69R58M8OIvHKpAw","token_type":"bearer","refresh_token":"eyJhbGciOiJSUzI1NiJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbIm9wZW5pZCJdLCJhdGkiOiIwMjI4MjMzZC1lY2NjLTQ1OWEtYWIwZC04OGRlNjA0NDFiMDAiLCJleHAiOjE0ODA5MjgxMzQsImF1dGhvcml0aWVzIjpbIlJFQUQiLCJXUklURSJdLCJqdGkiOiIyMzJkZDBiMS1kNzc2LTQ4MTItOTllNy0xNjRiZmNhOTY3ZTgiLCJjbGllbnRfaWQiOiJhcHBDbGllbnQifQ.WRgsM-ZFyYwU7ObLjNSzRFe0CUU0P7TyprKY825qL8teHPhgKLOxYDSXDDdjIwlLzOFXRz0skX6-h6kLXvXvvkPRFG3KK01j7jVmOfn1bXTYP0sgOwZvBvkDeZp0LFZIBL2ruH_ciWou6LleCgRIhtUVyKYMojr4-4eFZ_ou6J8ezykvzlf-xwTXJhwOqXf57jeYsXy9eitUf8A_x7Lrz9HffW0HA5JfhFX1P-_W1vS3uUqwQLZovhAzEK2LctZmUHAfUKIuE9Z4Z1_pQDDK7hWbv2eRHAlceRGRvztrEebv-5cksfEUWpiQmj913t20uXzEfJYU3e9JqbqxbaahKA","expires_in":43199,"scope":"openid","jti":"0228233d-eccc-459a-ab0d-88de60441b00"}
147 | ```
148 |
149 | #### 5、implicit方式(隐式许可)
150 |
151 | 使用`token`方式获取`accesstoken`(/oauth/authorize?response_type=token)可通过浏览器输入用户名密码直接获取`accesstoken`
152 |
153 | ```
154 | http://localhost:9999/oauth/authorize?response_type=token&client_id=acme&redirect_uri=http://notes.coding.me
155 | ```
156 |
157 | 输入用户名、密码,最终重定向到`http://notes.coding.me/#access_token=eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0NzgzNzg4NjEsInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiUkVBRCIsIldSSVRFIl0sImp0aSI6ImEyMzE3NTlmLTZhMzktNGQ3YS1iMmMyLWIwOTdlMWZjZTEwMiIsImNsaWVudF9pZCI6ImFwcENsaWVudCIsInNjb3BlIjpbIm9wZW5pZCJdfQ.ASSLsAJa8CsgZDsv5vH8BqYTbmoCCeYKqSmv4m9jl2XpWc2edvauy89Vxvj8z6kKGr8QqDg786u6MMW7fX5CAjP34Mfs9XVI8gfg20Xk0sHoS3WPx0mseIXdJbaxhj0526X5947-eeMr_LDC5N_XlPQ3Qq_PcY3tmyh92IWUri2rRJMKEPHrmVqqWPcPcCSHoEaaMWNTq_gsdbsZiyX4jaW24LVQ0HZ4oYMnmUzbLCvIyPcKF7WR-KKEnOykYX5FJPjnbUz6EK5yG_icdkULsxmDr05JrEgkKR0n_JfL9_gOqpI8mpJFBkAUghM1y9No_fGvvhb22o-H8ar5wnGqYA&token_type=bearer&expires_in=43199&scope=openid&jti=a231759f-6a39-4d7a-b2c2-b097e1fce102`
158 |
159 |
160 | ### 运行OAuth2 Client,使用accesstoken访问资源
161 |
162 | ```
163 | TOKEN=eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0NzgzNzQ3NTcsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJSRUFEIl0sImp0aSI6Ijg4Zjk4ZWYxLTZjOGItNDA1MC1iOTc3LWFlYjcxMzhlNjg2OCIsImNsaWVudF9pZCI6ImFwcENsaWVudCIsInNjb3BlIjpbIk9BdXRoMiJdfQ.sop6d8acs6piMgiN1FH8EVw4Qglh69wU5vvdMQZ87YSVjtaTCQqpf4kR65jXtqTNuTTZ8azf_aD5GoIBaqVrDDGEHk8dZLciobgD1vexpX2XnrfAFUt0xHg1LXIO_mJtf7x4CBiF4ysGWdlhWQbX2wq5YNvG3QhIkRHdnvxBNSiLJPSaa2sqHKxdXs4J7tLnNN415K1TI2pV7_6C3p-BQD2qfdIWZXB2JLYSmTVjnvUQtjIvLDNu8OL7mvyfP2F_d-b-PAPYU4ul9RZfnB9hYr02i8M-7lYh7pK-SoA0MlMlIP-QSDpACTqWuWpyP_q8N-tFDwPZ997ES2FOaWArFA
164 | ```
165 |
166 | 以`GET`方式获取资源
167 |
168 | ```
169 | $ curl -H "Authorization: Bearer $TOKEN" "localhost:9090/users"
170 | ```
171 |
172 | 以`POST`方式获取资源
173 |
174 | ```
175 | $ curl -XPOST -H "Authorization: Bearer $TOKEN" "localhost:9090/users"
176 | ```
177 |
178 | 获取用户凭证
179 |
180 | ```
181 | $ curl -H "Authorization: Bearer $TOKEN" "localhost:9999/user"
182 | {"details":{"remoteAddress":"0:0:0:0:0:0:0:1","sessionId":null,"tokenValue":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NzgzODY3NjcsInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiUkVBRCIsIldSSVRFIl0sImp0aSI6Ijc2OTYzMzkyLWUxYzItNDdmMC05ODZjLWZhODQxZmZhYTU1NyIsImNsaWVudF9pZCI6ImFwcENsaWVudCIsInNjb3BlIjpbIm9wZW5pZCJdfQ.WiTSjojK0ow1x7fP0y3EZohCozU2kB4Sxi2xPd7A5RSb48N-KDM8PYjSX--D1iiEQiRt2xOdBij0J0A7pIHfqZMijphyJQtPQjyv_82AIP7hFuW-PFMo2P1VVSTWrZeYzCMCr8p1QaHbM5bJ1KTeiT_Ak8vbqmnd7r4KpqoyEehca69tr02nTPhi7YOfAjUm9czwqhhi2w4bL9b2epYhmv_d2B2LwCY4NZxv64xfeCti8ya-AG_hYoqNRGj2nIHaAR_uCWmDD6_tdESy5oKZJ-881Dr0VFduJu4cAcSxJX-PqI6HklknTm_Vzqik9lI9S02z3fj5sBM7trWvNuBnrQ","tokenType":"Bearer","decodedDetails":null},"authorities":[{"authority":"READ"},{"authority":"WRITE"}],"authenticated":true,"userAuthentication":{"details":null,"authorities":[{"authority":"READ"},{"authority":"WRITE"}],"authenticated":true,"principal":"admin","credentials":"N/A","name":"admin"},"principal":"admin","credentials":"","oauth2Request":{"clientId":"acme","scope":["openid"],"requestParameters":{"client_id":"acme"},"resourceIds":[],"authorities":[],"approved":true,"refresh":false,"redirectUri":null,"responseTypes":[],"extensions":{},"grantType":null,"refreshTokenRequest":null},"clientOnly":false,"name":"admin"}
183 | ```
184 |
185 | 阅读以下类源码
186 |
187 | org.springframework.security.oauth2.provider.endpoint.TokenEndpoint /oauth/token接口
188 |
189 | org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint /oauth/authorize接口
190 |
191 | org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter 自定义WebSecurityConfigurer
192 |
193 | 注意:以上请求url中的请求参数名不能随意修改。
194 |
195 |
196 | ### 参考文章
197 |
198 | http://callistaenterprise.se/blogg/teknik/2015/04/27/building-microservices-part-3-secure-APIs-with-OAuth/
199 |
200 | http://docs.spring.io/spring-security/site/docs/4.2.2.RELEASE/reference/htmlsingle/#user-schema
201 |
202 | https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | com.example
7 | spring-boot-oauth2-example
8 | 1.0
9 | pom
10 |
11 | spring-boot-oauth2-example
12 | Demo project for Spring Boot
13 |
14 |
15 | OAuth2Server
16 | OAuth2Client
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------