├── .gitignore ├── springsecurity101-cloud-oauth2-userservice ├── src │ └── main │ │ ├── resources │ │ ├── application.yml │ │ └── public.cert │ │ └── java │ │ └── me │ │ └── josephzhu │ │ └── springsecurity101 │ │ └── cloud │ │ └── oauth2 │ │ └── userservice │ │ ├── HelloController.java │ │ ├── UserServiceApplication.java │ │ ├── UserController.java │ │ └── ResourceServerConfiguration.java └── pom.xml ├── springsecurity101-cloud-oauth2-server ├── src │ └── main │ │ ├── resources │ │ ├── jwt.jks │ │ ├── application.yml │ │ └── templates │ │ │ └── login.html │ │ └── java │ │ └── me │ │ └── josephzhu │ │ └── springsecurity101 │ │ └── cloud │ │ └── oauth2 │ │ └── server │ │ ├── OAuth2ServerApplication.java │ │ ├── CustomTokenEnhancer.java │ │ ├── WebSecurityConfig.java │ │ └── OAuth2ServerConfiguration.java └── pom.xml ├── docker-compose.yml ├── springsecurity101-cloud-oauth2-client ├── src │ └── main │ │ ├── java │ │ └── me │ │ │ └── josephzhu │ │ │ └── springsecurity101 │ │ │ └── cloud │ │ │ └── auth │ │ │ └── client │ │ │ ├── OAuth2ClientApplication.java │ │ │ ├── WebSecurityConfig.java │ │ │ ├── DemoController.java │ │ │ ├── OAuthClientConfig.java │ │ │ └── WebMvcConfig.java │ │ └── resources │ │ ├── templates │ │ ├── index.html │ │ └── securedPage.html │ │ └── application.yml └── pom.xml ├── pom.xml └── oauth.sql /.gitignore: -------------------------------------------------------------------------------- 1 | */target 2 | .idea 3 | *.iml 4 | -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-userservice/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8081 -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-server/src/main/resources/jwt.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JosephZhu1983/SpringSecurity101/HEAD/springsecurity101-cloud-oauth2-server/src/main/resources/jwt.jks -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-server/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | 4 | spring: 5 | application: 6 | name: oauth2-server 7 | datasource: 8 | url: jdbc:mysql://localhost:6657/oauth?useSSL=false 9 | username: root 10 | password: kIo9u7Oi0eg -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.6" 2 | services: 3 | redis: 4 | image: redis:3 5 | container_name: oauth2-redis-3 6 | command: redis-server --appendonly yes 7 | ports: 8 | - "6379:6379" 9 | mysql57: 10 | image: mysql:5.7 11 | container_name: oauth2-mysql57 12 | environment: 13 | MYSQL_ROOT_PASSWORD: kIo9u7Oi0eg 14 | ports: 15 | - "6657:3306" 16 | volumes: 17 | - ./mysql-init:/docker-entrypoint-initdb.d/ -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-userservice/src/main/java/me/josephzhu/springsecurity101/cloud/oauth2/userservice/HelloController.java: -------------------------------------------------------------------------------- 1 | package me.josephzhu.springsecurity101.cloud.oauth2.userservice; 2 | 3 | import org.springframework.web.bind.annotation.GetMapping; 4 | import org.springframework.web.bind.annotation.RestController; 5 | 6 | @RestController 7 | public class HelloController { 8 | @GetMapping("hello") 9 | public String hello() { 10 | return "Hello"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-client/src/main/java/me/josephzhu/springsecurity101/cloud/auth/client/OAuth2ClientApplication.java: -------------------------------------------------------------------------------- 1 | package me.josephzhu.springsecurity101.cloud.auth.client; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class OAuth2ClientApplication { 8 | public static void main(String[] args) { 9 | SpringApplication.run(OAuth2ClientApplication.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-server/src/main/java/me/josephzhu/springsecurity101/cloud/oauth2/server/OAuth2ServerApplication.java: -------------------------------------------------------------------------------- 1 | package me.josephzhu.springsecurity101.cloud.oauth2.server; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class OAuth2ServerApplication { 8 | public static void main(String[] args) { 9 | SpringApplication.run(OAuth2ServerApplication.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-userservice/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----- -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-userservice/src/main/java/me/josephzhu/springsecurity101/cloud/oauth2/userservice/UserServiceApplication.java: -------------------------------------------------------------------------------- 1 | package me.josephzhu.springsecurity101.cloud.oauth2.userservice; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class UserServiceApplication { 8 | public static void main(String[] args) { 9 | SpringApplication.run(UserServiceApplication.class, args); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-client/src/main/resources/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Spring Security SSO Client 6 | 8 | 9 | 10 | 11 |
12 |
13 |

Spring Security SSO Client

14 | Login 15 |
16 |
17 | 18 | -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-client/src/main/resources/templates/securedPage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Spring Security SSO Client 6 | 8 | 9 | 10 | 11 |
12 |
13 |

Secured Page

14 | Welcome, Name 15 |
16 | Your authorities are authorities 17 |
18 |
19 | 20 | -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-userservice/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | springsecurity101 7 | me.josephzhu 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | springsecurity101-cloud-oauth2-userservice 13 | 14 | 15 | 16 | org.springframework.cloud 17 | spring-cloud-starter-oauth2 18 | 19 | 20 | org.springframework.boot 21 | spring-boot-starter-web 22 | 23 | 24 | -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-client/src/main/java/me/josephzhu/springsecurity101/cloud/auth/client/WebSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package me.josephzhu.springsecurity101.cloud.auth.client; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.core.annotation.Order; 5 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 6 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 7 | 8 | @Configuration 9 | @Order(200) 10 | public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 11 | /** 12 | * /路径和/login路径允许访问,其它路径需要身份认证后才能访问 13 | * 14 | * @param http 15 | * @throws Exception 16 | */ 17 | @Override 18 | protected void configure(HttpSecurity http) throws Exception { 19 | http 20 | .authorizeRequests() 21 | .antMatchers("/", "/login**") 22 | .permitAll() 23 | .anyRequest() 24 | .authenticated(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-client/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8083 3 | servlet: 4 | context-path: /ui 5 | security: 6 | oauth2: 7 | client: 8 | clientId: userservice3 9 | clientSecret: 1234 10 | accessTokenUri: http://localhost:8080/oauth/token 11 | userAuthorizationUri: http://localhost:8080/oauth/authorize 12 | scope: FOO 13 | resource: 14 | jwt: 15 | key-value: | 16 | -----BEGIN PUBLIC KEY----- 17 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwR84LFHwnK5GXErnwkmD 18 | mPOJl4CSTtYXCqmCtlbF+5qVOosu0YsM2DsrC9O2gun6wVFKkWYiMoBSjsNMSI3Z 19 | w5JYgh+ldHvA+MIex2QXfOZx920M1fPUiuUPgmnTFS+Z3lmK3/T6jJnmciUPY1pe 20 | h4MXL6YzeI0q4W9xNBBeKT6FDGpduc0FC3OlXHfLbVOThKmAUpAWFDwf9/uUA//l 21 | 3PLchmV6VwTcUaaHp5W8Af/GU4lPGZbTAqOxzB9ukisPFuO1DikacPhrOQgdxtqk 22 | LciRTa884uQnkFwSguOEUYf3ni8GNRJauIuW0rVXhMOs78pKvCKmo53M0tqeC6ul 23 | +QIDAQAB 24 | -----END PUBLIC KEY----- 25 | spring: 26 | thymeleaf: 27 | cache: false 28 | 29 | #logging: 30 | # level: 31 | # ROOT: DEBUG 32 | 33 | -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | springsecurity101 8 | me.josephzhu 9 | 1.0-SNAPSHOT 10 | 11 | 12 | springsecurity101-cloud-oauth2-client 13 | 4.0.0 14 | 15 | 16 | 17 | org.springframework.cloud 18 | spring-cloud-starter-oauth2 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-web 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-thymeleaf 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-client/src/main/java/me/josephzhu/springsecurity101/cloud/auth/client/DemoController.java: -------------------------------------------------------------------------------- 1 | package me.josephzhu.springsecurity101.cloud.auth.client; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.http.ResponseEntity; 5 | import org.springframework.security.oauth2.client.OAuth2RestTemplate; 6 | import org.springframework.security.oauth2.provider.OAuth2Authentication; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | import org.springframework.web.servlet.ModelAndView; 10 | 11 | @RestController 12 | public class DemoController { 13 | @Autowired 14 | OAuth2RestTemplate restTemplate; 15 | 16 | @GetMapping("/securedPage") 17 | public ModelAndView securedPage(OAuth2Authentication authentication) { 18 | return new ModelAndView("securedPage").addObject("authentication", authentication); 19 | } 20 | 21 | @GetMapping("/remoteCall") 22 | public String remoteCall() { 23 | ResponseEntity responseEntity = restTemplate.getForEntity("http://localhost:8081/user/name", String.class); 24 | return responseEntity.getBody(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-client/src/main/java/me/josephzhu/springsecurity101/cloud/auth/client/OAuthClientConfig.java: -------------------------------------------------------------------------------- 1 | package me.josephzhu.springsecurity101.cloud.auth.client; 2 | 3 | import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.security.oauth2.client.OAuth2ClientContext; 7 | import org.springframework.security.oauth2.client.OAuth2RestTemplate; 8 | import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; 9 | 10 | @Configuration 11 | @EnableOAuth2Sso //这个注解包含了@EnableOAuth2Client 12 | public class OAuthClientConfig { 13 | /** 14 | * 定义了OAuth2RestTemplate,网上一些比较老的资料给出的是手动读取配置文件来实现,最新版本已经可以自动注入OAuth2ProtectedResourceDetails 15 | * 16 | * @param oAuth2ClientContext 17 | * @param details 18 | * @return 19 | */ 20 | @Bean 21 | public OAuth2RestTemplate oauth2RestTemplate(OAuth2ClientContext oAuth2ClientContext, 22 | OAuth2ProtectedResourceDetails details) { 23 | return new OAuth2RestTemplate(details, oAuth2ClientContext); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-client/src/main/java/me/josephzhu/springsecurity101/cloud/auth/client/WebMvcConfig.java: -------------------------------------------------------------------------------- 1 | package me.josephzhu.springsecurity101.cloud.auth.client; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.context.request.RequestContextListener; 6 | import org.springframework.web.servlet.config.annotation.EnableWebMvc; 7 | import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; 8 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 9 | 10 | @Configuration 11 | @EnableWebMvc 12 | public class WebMvcConfig implements WebMvcConfigurer { 13 | 14 | /** 15 | * 配置RequestContextListener用于启用session scope的Bean 16 | * 17 | * @return 18 | */ 19 | @Bean 20 | public RequestContextListener requestContextListener() { 21 | return new RequestContextListener(); 22 | } 23 | 24 | /** 25 | * 配置index路径的首页Controller 26 | * @param registry 27 | */ 28 | @Override 29 | public void addViewControllers(ViewControllerRegistry registry) { 30 | registry.addViewController("/") 31 | .setViewName("forward:/index"); 32 | registry.addViewController("/index"); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-server/src/main/java/me/josephzhu/springsecurity101/cloud/oauth2/server/CustomTokenEnhancer.java: -------------------------------------------------------------------------------- 1 | package me.josephzhu.springsecurity101.cloud.oauth2.server; 2 | 3 | import org.springframework.security.core.Authentication; 4 | import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; 5 | import org.springframework.security.oauth2.common.OAuth2AccessToken; 6 | import org.springframework.security.oauth2.provider.OAuth2Authentication; 7 | import org.springframework.security.oauth2.provider.token.TokenEnhancer; 8 | 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | public class CustomTokenEnhancer implements TokenEnhancer { 13 | 14 | @Override 15 | public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { 16 | Authentication userAuthentication = authentication.getUserAuthentication(); 17 | if (userAuthentication != null) { 18 | Object principal = authentication.getUserAuthentication().getPrincipal(); 19 | //把用户标识以userDetails这个Key加入到JWT的额外信息中去 20 | Map additionalInfo = new HashMap<>(); 21 | additionalInfo.put("userDetails", principal); 22 | ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo); 23 | } 24 | return accessToken; 25 | } 26 | } -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-server/src/main/resources/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | OAuth2 Demo 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |

Login Form

14 | 15 |

16 | 用户名或密码错误... 17 |

18 | 19 |
20 |
21 | 23 |
24 |
25 | 27 |
28 |
29 | 30 |
31 |
32 | 33 |
34 |
35 | 36 | -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | springsecurity101 7 | me.josephzhu 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | springsecurity101-cloud-oauth2-server 13 | 14 | 15 | 16 | org.springframework.cloud 17 | spring-cloud-starter-oauth2 18 | 19 | 20 | org.springframework.boot 21 | spring-boot-starter-jdbc 22 | 23 | 24 | mysql 25 | mysql-connector-java 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-web 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-thymeleaf 34 | 35 | 36 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.josephzhu 8 | springsecurity101 9 | pom 10 | 1.0-SNAPSHOT 11 | 12 | 13 | org.springframework.boot 14 | spring-boot-starter-parent 15 | 2.2.1.RELEASE 16 | 17 | 18 | 19 | 20 | springsecurity101-cloud-oauth2-client 21 | springsecurity101-cloud-oauth2-server 22 | springsecurity101-cloud-oauth2-userservice 23 | 24 | 25 | 26 | UTF-8 27 | UTF-8 28 | 1.8 29 | 30 | 31 | 32 | 33 | org.projectlombok 34 | lombok 35 | true 36 | 37 | 38 | 39 | 40 | 41 | 42 | org.springframework.cloud 43 | spring-cloud-dependencies 44 | Greenwich.SR4 45 | pom 46 | import 47 | 48 | 49 | 50 | 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-maven-plugin 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-server/src/main/java/me/josephzhu/springsecurity101/cloud/oauth2/server/WebSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package me.josephzhu.springsecurity101.cloud.oauth2.server; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.security.authentication.AuthenticationManager; 7 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 9 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 10 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 11 | 12 | import javax.sql.DataSource; 13 | 14 | 15 | @Configuration 16 | public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 17 | @Autowired 18 | private DataSource dataSource; 19 | 20 | @Override 21 | @Bean 22 | public AuthenticationManager authenticationManagerBean() throws Exception { 23 | return super.authenticationManagerBean(); 24 | } 25 | 26 | /** 27 | * 配置用户账户的认证方式,显然,我们把用户存在了数据库中希望配置JDBC的方式。 28 | * 此外,我们还配置了使用BCryptPasswordEncoder哈希来保存用户的密码(生产环境的用户密码肯定不能是明文保存) 29 | * 30 | * @param auth 31 | * @throws Exception 32 | */ 33 | @Override 34 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 35 | auth.jdbcAuthentication() 36 | .dataSource(dataSource) 37 | .passwordEncoder(new BCryptPasswordEncoder()); 38 | } 39 | 40 | /** 41 | * 开放/login和/oauth/authorize两个路径的匿名访问,前者用于登录,后者用于换授权码,这两个端点访问的时候都在登录之前。 42 | * 设置/login使用表单验证进行登录。 43 | * @param http 44 | * @throws Exception 45 | */ 46 | @Override 47 | protected void configure(HttpSecurity http) throws Exception { 48 | http.authorizeRequests() 49 | .antMatchers("/login", "/oauth/authorize") 50 | .permitAll() 51 | .anyRequest().authenticated() 52 | .and() 53 | .formLogin().loginPage("/login"); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-userservice/src/main/java/me/josephzhu/springsecurity101/cloud/oauth2/userservice/UserController.java: -------------------------------------------------------------------------------- 1 | package me.josephzhu.springsecurity101.cloud.oauth2.userservice; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.security.access.prepost.PreAuthorize; 5 | import org.springframework.security.oauth2.common.OAuth2AccessToken; 6 | import org.springframework.security.oauth2.provider.OAuth2Authentication; 7 | import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; 8 | import org.springframework.security.oauth2.provider.token.TokenStore; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | @RestController 15 | @RequestMapping("user") 16 | public class UserController { 17 | 18 | @Autowired 19 | private TokenStore tokenStore; 20 | 21 | /*** 22 | * 读权限或写权限可访问,返回登录用户名 23 | * @param authentication 24 | * @return 25 | */ 26 | @PreAuthorize("hasAuthority('READ') or hasAuthority('WRITE')") 27 | @GetMapping("name") 28 | public String name(OAuth2Authentication authentication) { 29 | return authentication.getName(); 30 | } 31 | 32 | /** 33 | * 读权限或写权限可访问,返回登录用户信息 34 | * 35 | * @param authentication 36 | * @return 37 | */ 38 | @PreAuthorize("hasAuthority('READ') or hasAuthority('WRITE')") 39 | @GetMapping 40 | public OAuth2Authentication read(OAuth2Authentication authentication) { 41 | return authentication; 42 | } 43 | 44 | /** 45 | * 只有写权限可以访问,返回访问令牌中的额外信息 46 | * @param authentication 47 | * @return 48 | */ 49 | @PreAuthorize("hasAuthority('WRITE')") 50 | @PostMapping 51 | public Object write(OAuth2Authentication authentication) { 52 | OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails(); 53 | OAuth2AccessToken accessToken = tokenStore.readAccessToken(details.getTokenValue()); 54 | return accessToken.getAdditionalInformation().getOrDefault("userDetails", null); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-userservice/src/main/java/me/josephzhu/springsecurity101/cloud/oauth2/userservice/ResourceServerConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.josephzhu.springsecurity101.cloud.oauth2.userservice; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.core.io.ClassPathResource; 6 | import org.springframework.core.io.Resource; 7 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 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 | import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; 14 | import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; 15 | import org.springframework.util.FileCopyUtils; 16 | 17 | import java.io.IOException; 18 | 19 | @Configuration 20 | @EnableResourceServer //启用资源服务器 21 | @EnableGlobalMethodSecurity(prePostEnabled = true) //启用方法注解方式来进行权限控制 22 | public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { 23 | 24 | /** 25 | * 声明了资源服务器的ID是userservice,声明了资源服务器的TokenStore是JWT 26 | * @param resources 27 | * @throws Exception 28 | */ 29 | @Override 30 | public void configure(ResourceServerSecurityConfigurer resources) { 31 | resources.resourceId("userservice").tokenStore(tokenStore()); 32 | } 33 | 34 | /** 35 | * 配置TokenStore 36 | * 37 | * @return 38 | */ 39 | @Bean 40 | public TokenStore tokenStore() { 41 | return new JwtTokenStore(jwtAccessTokenConverter()); 42 | } 43 | 44 | /** 45 | * 配置公钥 46 | * @return 47 | */ 48 | @Bean 49 | protected JwtAccessTokenConverter jwtAccessTokenConverter() { 50 | JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); 51 | Resource resource = new ClassPathResource("public.cert"); 52 | String publicKey = null; 53 | try { 54 | publicKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream())); 55 | } catch (IOException e) { 56 | e.printStackTrace(); 57 | } 58 | converter.setVerifierKey(publicKey); 59 | return converter; 60 | } 61 | 62 | /** 63 | * 配置了除了/user路径之外的请求可以匿名访问 64 | * @param http 65 | * @throws Exception 66 | */ 67 | @Override 68 | public void configure(HttpSecurity http) throws Exception { 69 | http.authorizeRequests() 70 | .antMatchers("/user/**").authenticated() 71 | .anyRequest().permitAll(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /oauth.sql: -------------------------------------------------------------------------------- 1 | 2 | 3 | SET NAMES utf8mb4; 4 | SET FOREIGN_KEY_CHECKS = 0; 5 | 6 | -- ---------------------------- 7 | -- Table structure for authorities 8 | -- ---------------------------- 9 | drop table IF EXISTS `authorities`; 10 | create TABLE `authorities` ( 11 | `username` varchar(50) NOT NULL, 12 | `authority` varchar(50) NOT NULL, 13 | UNIQUE KEY `ix_auth_username` (`username`,`authority`), 14 | CONSTRAINT `fk_authorities_users` FOREIGN KEY (`username`) REFERENCES `users` (`username`) 15 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 16 | 17 | -- ---------------------------- 18 | -- Records of authorities 19 | -- ---------------------------- 20 | begin; 21 | INSERT INTO `authorities` VALUES ('reader', 'READ'); 22 | INSERT INTO `authorities` VALUES ('writer', 'READ,WRITE'); 23 | COMMIT; 24 | 25 | -- ---------------------------- 26 | -- Table structure for oauth_approvals 27 | -- ---------------------------- 28 | DROP TABLE IF EXISTS `oauth_approvals`; 29 | create TABLE `oauth_approvals` ( 30 | `userId` varchar(256) DEFAULT NULL, 31 | `clientId` varchar(256) DEFAULT NULL, 32 | `partnerKey` varchar(32) DEFAULT NULL, 33 | `scope` varchar(256) DEFAULT NULL, 34 | `status` varchar(10) DEFAULT NULL, 35 | `expiresAt` datetime DEFAULT NULL, 36 | `lastModifiedAt` datetime DEFAULT NULL 37 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 38 | 39 | -- ---------------------------- 40 | -- Table structure for oauth_client_details 41 | -- ---------------------------- 42 | DROP TABLE IF EXISTS `oauth_client_details`; 43 | create TABLE `oauth_client_details` ( 44 | `client_id` varchar(255) NOT NULL, 45 | `resource_ids` varchar(255) DEFAULT NULL, 46 | `client_secret` varchar(255) DEFAULT NULL, 47 | `scope` varchar(255) DEFAULT NULL, 48 | `authorized_grant_types` varchar(255) DEFAULT NULL, 49 | `web_server_redirect_uri` varchar(255) DEFAULT NULL, 50 | `authorities` varchar(255) DEFAULT NULL, 51 | `access_token_validity` int(11) DEFAULT NULL, 52 | `refresh_token_validity` int(11) DEFAULT NULL, 53 | `additional_information` varchar(4096) DEFAULT NULL, 54 | `autoapprove` varchar(255) DEFAULT NULL, 55 | PRIMARY KEY (`client_id`) 56 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 57 | 58 | -- ---------------------------- 59 | -- Records of oauth_client_details 60 | -- ---------------------------- 61 | BEGIN; 62 | INSERT INTO `oauth_client_details` VALUES ('userservice1', 'userservice', '1234', 'FOO', 'password,refresh_token', '', 'READ,WRITE', 7200, NULL, NULL, 'true'); 63 | INSERT INTO `oauth_client_details` VALUES ('userservice2', 'userservice', '1234', 'FOO', 'client_credentials,refresh_token', '', 'READ,WRITE', 7200, NULL, NULL, 'true'); 64 | INSERT INTO `oauth_client_details` VALUES ('userservice3', 'userservice', '1234', 'FOO', 'authorization_code,refresh_token', 'https://baidu.com,http://localhost:8082/ui/login,http://localhost:8083/ui/login,http://localhost:8082/ui/remoteCall', 'READ,WRITE', 7200, NULL, NULL, 'false'); 65 | COMMIT; 66 | 67 | -- ---------------------------- 68 | -- Table structure for oauth_code 69 | -- ---------------------------- 70 | DROP TABLE IF EXISTS `oauth_code`; 71 | create TABLE `oauth_code` ( 72 | `code` varchar(255) DEFAULT NULL, 73 | `authentication` blob 74 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 75 | 76 | -- ---------------------------- 77 | -- Table structure for users 78 | -- ---------------------------- 79 | DROP TABLE IF EXISTS `users`; 80 | create TABLE `users` ( 81 | `username` varchar(50) NOT NULL, 82 | `password` varchar(100) NOT NULL, 83 | `enabled` tinyint(1) NOT NULL, 84 | PRIMARY KEY (`username`) 85 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 86 | 87 | -- ---------------------------- 88 | -- Records of users 89 | -- ---------------------------- 90 | BEGIN; 91 | INSERT INTO `users` VALUES ('reader', '$2a$04$C6pPJvC1v6.enW6ZZxX.luTdpSI/1gcgTVN7LhvQV6l/AfmzNU/3i', 1); 92 | INSERT INTO `users` VALUES ('writer', '$2a$04$M9t2oVs3/VIreBMocOujqOaB/oziWL0SnlWdt8hV4YnlhQrORA0fS', 1); 93 | COMMIT; 94 | 95 | SET FOREIGN_KEY_CHECKS = 1; 96 | -------------------------------------------------------------------------------- /springsecurity101-cloud-oauth2-server/src/main/java/me/josephzhu/springsecurity101/cloud/oauth2/server/OAuth2ServerConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.josephzhu.springsecurity101.cloud.oauth2.server; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 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.security.authentication.AuthenticationManager; 8 | import org.springframework.security.crypto.password.NoOpPasswordEncoder; 9 | import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; 10 | import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; 11 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; 12 | import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; 13 | import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; 14 | import org.springframework.security.oauth2.provider.approval.JdbcApprovalStore; 15 | import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices; 16 | import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices; 17 | import org.springframework.security.oauth2.provider.token.TokenEnhancer; 18 | import org.springframework.security.oauth2.provider.token.TokenEnhancerChain; 19 | import org.springframework.security.oauth2.provider.token.TokenStore; 20 | import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; 21 | import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; 22 | import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory; 23 | import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; 24 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 25 | 26 | import javax.sql.DataSource; 27 | import java.util.Arrays; 28 | 29 | @Configuration 30 | @EnableAuthorizationServer //开启授权服务器 31 | public class OAuth2ServerConfiguration extends AuthorizationServerConfigurerAdapter { 32 | @Autowired 33 | private DataSource dataSource; 34 | @Autowired 35 | private AuthenticationManager authenticationManager; 36 | 37 | /** 38 | * 我们配置了使用数据库来维护客户端信息。虽然在各种Demo中我们经常看到的是在内存中维护客户端信息,通过配置直接写死在这里。 39 | * 但是,对于实际的应用我们一般都会用数据库来维护这个信息,甚至还会建立一套工作流来允许客户端自己申请ClientID。 40 | * @param clients 41 | * @throws Exception 42 | */ 43 | @Override 44 | public void configure(ClientDetailsServiceConfigurer clients) throws Exception { 45 | clients.jdbc(dataSource); 46 | } 47 | 48 | /** 49 | * 这里干了两个事情,首先打开了验证Token的访问权限(以便之后我们演示)。 50 | * 然后允许ClientSecret明文方式保存并且可以通过表单提交(而不仅仅是Basic Auth方式提交),之后会演示到这个。 51 | * @param security 52 | * @throws Exception 53 | */ 54 | @Override 55 | public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { 56 | security.checkTokenAccess("permitAll()").allowFormAuthenticationForClients().passwordEncoder(NoOpPasswordEncoder.getInstance()); 57 | } 58 | 59 | /** 60 | * 干了几个事情: 61 | * 1、配置我们的Token存放方式不是内存、数据库或Redis方式,而是JWT方式。 62 | * JWT是Json Web Token缩写也就是使用JSON数据格式包装的Token,由.句号把整个JWT分隔为头、数据体、签名三部分。 63 | * JWT保存Token虽然易于使用但是不是那么安全,一般用于内部,并且需要走HTTPS+配置比较短的失效时间。 64 | * 2、配置了JWT Token的非对称加密来进行签名 65 | * 3、配置了一个自定义的Token增强器,把更多信息放入Token中 66 | * 4、配置了使用JDBC数据库方式来保存用户的授权批准记录 67 | * @param endpoints 68 | * @throws Exception 69 | */ 70 | @Override 71 | public void configure(AuthorizationServerEndpointsConfigurer endpoints) { 72 | TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); 73 | tokenEnhancerChain.setTokenEnhancers( 74 | Arrays.asList(tokenEnhancer(), jwtTokenEnhancer())); 75 | 76 | endpoints.approvalStore(approvalStore()) 77 | .authorizationCodeServices(authorizationCodeServices()) 78 | .tokenStore(tokenStore()) 79 | .tokenEnhancer(tokenEnhancerChain) 80 | .authenticationManager(authenticationManager); 81 | } 82 | 83 | /** 84 | * 使用JDBC数据库方式来保存授权码 85 | * 86 | * @return 87 | */ 88 | @Bean 89 | public AuthorizationCodeServices authorizationCodeServices() { 90 | return new JdbcAuthorizationCodeServices(dataSource); 91 | } 92 | 93 | /** 94 | * 使用JWT令牌存储 95 | * @return 96 | */ 97 | @Bean 98 | public TokenStore tokenStore() { 99 | return new JwtTokenStore(jwtTokenEnhancer()); 100 | } 101 | 102 | /** 103 | * 使用JDBC数据库方式来保存用户的授权批准记录 104 | * @return 105 | */ 106 | @Bean 107 | public JdbcApprovalStore approvalStore() { 108 | return new JdbcApprovalStore(dataSource); 109 | } 110 | 111 | /** 112 | * 自定义的Token增强器,把更多信息放入Token中 113 | * @return 114 | */ 115 | @Bean 116 | public TokenEnhancer tokenEnhancer() { 117 | return new CustomTokenEnhancer(); 118 | } 119 | 120 | /** 121 | * 配置JWT令牌使用非对称加密方式来验证 122 | * @return 123 | */ 124 | @Bean 125 | protected JwtAccessTokenConverter jwtTokenEnhancer() { 126 | KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "mySecretKey".toCharArray()); 127 | JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); 128 | converter.setKeyPair(keyStoreKeyFactory.getKeyPair("jwt")); 129 | return converter; 130 | } 131 | 132 | /** 133 | * 配置登录页面的视图信息(其实可以独立一个配置类更规范) 134 | */ 135 | @Configuration 136 | static class MvcConfig implements WebMvcConfigurer { 137 | @Override 138 | public void addViewControllers(ViewControllerRegistry registry) { 139 | registry.addViewController("login").setViewName("login"); 140 | } 141 | } 142 | } 143 | --------------------------------------------------------------------------------