查看: 598|回复: 0

[Java学习] Spring Cloud下基于OAUTH2认证授权的实现示例

发表于 2018-4-15 08:00:01

在Spring Cloud需要使用OAUTH2来实现多个微服务的统一认证授权,通过向OAUTH服务发送某个类型的grant type进行集中认证和授权,从而获得access_token,而这个token是受其他微服务信任的,我们在后续的访问可以通过access_token来进行,从而实现了微服务的统一认证授权。

本示例提供了四大部分:

  1. discovery-service:服务注册和发现的基本模块
  2. auth-server:OAUTH2认证授权中心
  3. order-service:普通微服务,用来验证认证和授权
  4. api-gateway:边界网关(所有微服务都在它之后)

OAUTH2中的角色:

  1. Resource Server:被授权访问的资源
  2. Authotization Server:OAUTH2认证授权中心
  3. Resource Owner: 用户
  4. Client:使用API的客户端(如Android 、IOS、web app)

Grant Type:

  1. Authorization Code:用在服务端应用之间
  2. Implicit:用在移动app或者web app(这些app是在用户的设备上的,如在手机上调起微信来进行认证授权)
  3. Resource Owner Password Credentials(password):应用直接都是受信任的(都是由一家公司开发的,本例子使用
  4. Client Credentials:用在应用API访问。

1.基础环境

使用Postgres作为账户存储,Redis作为Token存储,使用docker-compose在服务器上启动Postgres和Redis。

  1. Redis:
  2. image: sameersbn/redis:latest
  3. ports:
  4. - "6379:6379"
  5. volumes:
  6. - /srv/docker/redis:/var/lib/redis:Z
  7. restart: always
  8. PostgreSQL:
  9. restart: always
  10. image: sameersbn/postgresql:9.6-2
  11. ports:
  12. - "5432:5432"
  13. environment:
  14. - DEBUG=false
  15. - DB_USER=wang
  16. - DB_PASS=yunfei
  17. - DB_NAME=order
  18. volumes:
  19. - /srv/docker/postgresql:/var/lib/postgresql:Z
复制代码

2.auth-server

2.1 OAuth2服务配置

Redis用来存储token,服务重启后,无需重新获取token.

  1. @Configuration
  2. @EnableAuthorizationServer
  3. public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
  4. @Autowired
  5. private AuthenticationManager authenticationManager;
  6. @Autowired
  7. private RedisConnectionFactory connectionFactory;
  8. @Bean
  9. public RedisTokenStore tokenStore() {
  10. return new RedisTokenStore(connectionFactory);
  11. }
  12. @Override
  13. public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
  14. endpoints
  15. .authenticationManager(authenticationManager)
  16. .tokenStore(tokenStore());
  17. }
  18. @Override
  19. public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
  20. security
  21. .tokenKeyAccess("permitAll()")
  22. .checkTokenAccess("isAuthenticated()");
  23. }
  24. @Override
  25. public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
  26. clients.inMemory()
  27. .withClient("android")
  28. .scopes("xx") //此处的scopes是无用的,可以随意设置
  29. .secret("android")
  30. .authorizedGrantTypes("password", "authorization_code", "refresh_token")
  31. .and()
  32. .withClient("webapp")
  33. .scopes("xx")
  34. .authorizedGrantTypes("implicit");
  35. }
  36. }
复制代码

2.2 Resource服务配置

auth-server提供user信息,所以auth-server也是一个Resource Server

  1. @Configuration
  2. @EnableResourceServer
  3. public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
  4. @Override
  5. public void configure(HttpSecurity http) throws Exception {
  6. http
  7. .csrf().disable()
  8. .exceptionHandling()
  9. .authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED))
  10. .and()
  11. .authorizeRequests()
  12. .anyRequest().authenticated()
  13. .and()
  14. .httpBasic();
  15. }
  16. }
复制代码
  1. @RestController
  2. public class UserController {
  3. @GetMapping("/user")
  4. public Principal user(Principal user){
  5. return user;
  6. }
  7. }
复制代码

2.3 安全配置

  1. @Configuration
  2. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  3. @Bean
  4. public UserDetailsService userDetailsService(){
  5. return new DomainUserDetailsService();
  6. }
  7. @Bean
  8. public PasswordEncoder passwordEncoder() {
  9. return new BCryptPasswordEncoder();
  10. }
  11. @Override
  12. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  13. auth
  14. .userDetailsService(userDetailsService())
  15. .passwordEncoder(passwordEncoder());
  16. }
  17. @Bean
  18. public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
  19. return new SecurityEvaluationContextExtension();
  20. }
  21. //不定义没有password grant_type
  22. @Override
  23. @Bean
  24. public AuthenticationManager authenticationManagerBean() throws Exception {
  25. return super.authenticationManagerBean();
  26. }
  27. }
复制代码

2.4 权限设计

采用用户(SysUser) 角色(SysRole) 权限(SysAuthotity)设置,彼此之间的关系是多对多。通过DomainUserDetailsService 加载用户和权限。

2.5 配置

  1. spring:
  2. profiles:
  3. active: ${SPRING_PROFILES_ACTIVE:dev}
  4. application:
  5. name: auth-server
  6. jpa:
  7. open-in-view: true
  8. database: POSTGRESQL
  9. show-sql: true
  10. hibernate:
  11. ddl-auto: update
  12. datasource:
  13. platform: postgres
  14. url: jdbc:postgresql://192.168.1.140:5432/auth
  15. username: wang
  16. password: yunfei
  17. driver-class-name: org.postgresql.Driver
  18. redis:
  19. host: 192.168.1.140
  20. server:
  21. port: 9999
  22. eureka:
  23. client:
  24. serviceUrl:
  25. defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/
  26. logging.level.org.springframework.security: DEBUG
  27. logging.leve.org.springframework: DEBUG
  28. ##很重要
  29. security:
  30. oauth2:
  31. resource:
  32. filter-order: 3
复制代码

2.6 测试数据

data.sql里初始化了两个用户admin->ROLE_ADMIN->query_demo,wyf->ROLE_USER

3.order-service

3.1 Resource服务配置

  1. @Configuration
  2. @EnableResourceServer
  3. public class ResourceServerConfig extends ResourceServerConfigurerAdapter{
  4. @Override
  5. public void configure(HttpSecurity http) throws Exception {
  6. http
  7. .csrf().disable()
  8. .exceptionHandling()
  9. .authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED))
  10. .and()
  11. .authorizeRequests()
  12. .anyRequest().authenticated()
  13. .and()
  14. .httpBasic();
  15. }
  16. }
复制代码

3.2 用户信息配置

order-service是一个简单的微服务,使用auth-server进行认证授权,在它的配置文件指定用户信息在auth-server的地址即可:

  1. security:
  2. oauth2:
  3. resource:
  4. id: order-service
  5. user-info-uri: http://localhost:8080/uaa/user
  6. prefer-token-info: false
复制代码

3.3 权限测试控制器

具备authority未query-demo的才能访问,即为admin用户

  1. @RestController
  2. public class DemoController {
  3. @GetMapping("/demo")
  4. @PreAuthorize("hasAuthority('query-demo')")
  5. public String getDemo(){
  6. return "good";
  7. }
  8. }
复制代码

4 api-gateway

api-gateway在本例中有2个作用:

  1. 本身作为一个client,使用implicit
  2. 作为外部app访问的方向代理

4.1 关闭csrf并开启Oauth2 client支持

  1. @Configuration
  2. @EnableOAuth2Sso
  3. public class SecurityConfig extends WebSecurityConfigurerAdapter{
  4. @Override
  5. protected void configure(HttpSecurity http) throws Exception {
  6. http.csrf().disable();
  7. }
  8. }
复制代码

4.2 配置

  1. zuul:
  2. routes:
  3. uaa:
  4. path: /uaa/**
  5. sensitiveHeaders:
  6. serviceId: auth-server
  7. order:
  8. path: /order/**
  9. sensitiveHeaders:
  10. serviceId: order-service
  11. add-proxy-headers: true
  12. security:
  13. oauth2:
  14. client:
  15. access-token-uri: http://localhost:8080/uaa/oauth/token
  16. user-authorization-uri: http://localhost:8080/uaa/oauth/authorize
  17. client-id: webapp
  18. resource:
  19. user-info-uri: http://localhost:8080/uaa/user
  20. prefer-token-info: false
复制代码

5 演示

5.1 客户端调用

使用Postman向http://localhost:8080/uaa/oauth/token发送请求获得access_token(admin用户的如7f9b54d4-fd25-4a2c-a848-ddf8f119230b)

admin用户

wyf用户

5.2 api-gateway中的webapp调用

暂时没有做测试,下次补充。

6 源码地址

https://github.com/wiselyman/uaa-zuul

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持程序员之家。



回复

使用道具 举报