Spring Cloud 之 Oauth2
1.创建Eureka服务器
a.创建SpringBoot工程,依赖选择Eureka Server
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> ... </dependencies>
b.修改 application.yml 配置
spring: application: name: eureka-server server: port: 8081 eureka: instance: hostname: localhost client: service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ fetch-registry: false register-with-eureka: false
c.启动类添加注解@EnableEurekaServer
@SpringBootApplication @EnableEurekaServer public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } }
2.创建Oauth2服务器
a.创建SpringBoot工程,依赖选择Eureka Discovery 、Web、Spring Data Redis、Cloud Oauth2
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency> ..... </dependencies>
b.修改 application.yml 配置
spring: application: name: oauth-client redis: host: localhost database: 0 server: port: 8082 eureka: client: service-url: defaultZone: http://localhost:8081/eureka/
c.创建 AuthorizationServerConfiguration 继承 AuthorizationServerConfigurerAdapter
@Configuration @EnableAuthorizationServer public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { @Resource AuthenticationManager authenticationManager; @Resource RedisConnectionFactory redisConnectionFactory; @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { String finalSecret = "{bcrypt}" + new BCryptPasswordEncoder().encode("123456"); // 配置两个客户端,一个用于password认证一个用于client_credentials认证 clients.inMemory().withClient("client_1") .authorizedGrantTypes("client_credentials", "refresh_token") .scopes("server") .authorities("oauth2") .secret(finalSecret) .and().withClient("client_2") .authorizedGrantTypes("password", "refresh_token") .scopes("select") .authorities("oauth2") .secret(finalSecret); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(new RedisTokenStore(redisConnectionFactory)) .authenticationManager(authenticationManager) .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST); } @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { // 允许表单认证 security.allowFormAuthenticationForClients(); } }
d.创建 SecurityConfiguration 继承 WebSecurityConfigurerAdapter
@Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Bean @Override protected UserDetailsService userDetailsService() { //模拟用户-角色,可换为从DB查询 BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); String finalPassword = "{bcrypt}" + bCryptPasswordEncoder.encode("123456"); InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); manager.createUser(User.withUsername("user").password(finalPassword).authorities("ROLE_USER").build()); manager.createUser(User.withUsername("vip").password(finalPassword).authorities("ROLE_VIP").build()); manager.createUser(User.withUsername("admin").password(finalPassword).authorities("ROLE_ADMIN").build()); return manager; } @Bean PasswordEncoder passwordEncoder() { return PasswordEncoderFactories.createDelegatingPasswordEncoder(); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { AuthenticationManager manager = super.authenticationManagerBean(); return manager; } @Override protected void configure(HttpSecurity http) throws Exception { http.requestMatchers().anyRequest() .and() .authorizeRequests() .antMatchers("/oauth/**").permitAll(); } }
e.对外提供获取用户信息的接口
@RestController @RequestMapping("/users") public class UserController { Logger logger = LoggerFactory.getLogger(UserController.class); @RequestMapping(value = "/current", method = RequestMethod.GET) public Principal getUser(Principal principal) { logger.info(">>>>>>>>>>>>>>>>>>>>>>>>"); logger.info(principal.toString()); logger.info(">>>>>>>>>>>>>>>>>>>>>>>>"); return principal; } }
f.启动类加上@EnableResourceServer注解开启资源服务
@SpringBootApplication @EnableResourceServer @EnableEurekaClient public class OauthClientApplication { public static void main(String[] args) { SpringApplication.run(OauthClientApplication.class, args); } }
3.创建资源服务器
a.创建SpringBoot工程,依赖选择Eureka Discovery 、Web、Cloud Oauth2 以及 Feign
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> ..... </dependencies>
b.修改 application.yml 配置
eureka: client: service-url: defaultZone: http://localhost:8081/eureka/ server: port: 8083 spring: application: name: service-client security: oauth2: #用户信息url resource: user-info-uri: http://localhost:8082/users/current #客户端信息,与oauth-client的配置一致 client: id: client_1 client-secret: 123456 access-token-uri: http://localhost:8082/oauth/token grant-type: client_credentials,password scope: server
c.创建 ResourceServerConfiguration 继承 ResourceServerConfigurerAdapter
@Configuration @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true) public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/order/**").hasAnyRole("ADMIN", "VIP") .antMatchers("/product/**").authenticated(); } }
d.创建 OAuth2ClientConfig,添加注解@EnableOAuth2Client开启OAuth2 Client功能
@EnableOAuth2Client @EnableConfigurationProperties @Configuration public class OAuth2ClientConfig { @Bean @ConfigurationProperties(prefix = "security.oauth2.client") public ClientCredentialsResourceDetails clientCredentialsResourceDetails() { return new ClientCredentialsResourceDetails(); } @Bean public RequestInterceptor oauth2FeignRequestInterceptor() { return new OAuth2FeignRequestInterceptor(new DefaultOAuth2ClientContext(), clientCredentialsResourceDetails()); } @Bean public OAuth2RestTemplate clientCredentialsRestTemplate() { return new OAuth2RestTemplate(clientCredentialsResourceDetails()); } }
e.创建Controller测试
@RestController public class CommonController { Logger logger = LoggerFactory.getLogger(CommonController.class); @GetMapping("/product/{id}") public String getProduct(@PathVariable String id) { return "product id : " + id; } @GetMapping("/order/{id}") public String getOrder(@PathVariable String id) { return "order id : " + id; } @GetMapping("/getPrinciple") public OAuth2Authentication getPrinciple(OAuth2Authentication oAuth2Authentication, Principal principal, Authentication authentication) { logger.info(oAuth2Authentication.getUserAuthentication().getAuthorities().toString()); logger.info(oAuth2Authentication.toString()); logger.info("principal.toString() " + principal.toString()); logger.info("principal.getName() " + principal.getName()); logger.info("authentication: " + authentication.getAuthorities().toString()); return oAuth2Authentication; } }
f.启动类添加 @EnableEurekaClient 注解表示 Eureka 客户端
@SpringBootApplication @EnableEurekaClient public class ServiceHiApplication { public static void main(String[] args) { SpringApplication.run(ServiceHiApplication.class, args); } }
4.测试:
a.依次启动 Eureka服务器、Oauth2服务器、资源服务器 (Redis也需要启动)
b.打开浏览器访问 http://localhost:8082/oauth/token?username=user&password=123456&grant_type=password&scope=select&client_id=client_2&client_secret=123456 获取 access_token
c.分别不带 token 访问 http://localhost:8083/order/1 以及带 token 访问 http://localhost:8083/order/1?access_token=d3631d4b-f710-468b-aa3f-26d72a23064a
d.分别不带 token 访问 http://localhost:8083/product/1 以及带 token 访问 http://localhost:8083/product/1?access_token=d3631d4b-f710-468b-aa3f-26d72a23064a
5.参考文档:https://www.jianshu.com/p/3427549a148a