├── .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 | --------------------------------------------------------------------------------