Spring Cloud下基于OAUTH2认证授权的实现示例

时间:2022-09-06 10:57:48

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在服务器上启动postgresredis

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
redis:
 image: sameersbn/redis:latest
 ports:
 - "6379:6379"
 volumes:
 - /srv/docker/redis:/var/lib/redis:z
 restart: always
 
postgresql:
 restart: always
 image: sameersbn/postgresql:9.6-2
 ports:
 - "5432:5432"
 environment:
 - debug=false
 
 - db_user=wang
 - db_pass=yunfei
 - db_name=order
 volumes:
 - /srv/docker/postgresql:/var/lib/postgresql:z

2.auth-server

2.1 oauth2服务配置

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@configuration
@enableauthorizationserver
public class authorizationserverconfig extends authorizationserverconfigureradapter {
 @autowired
 private authenticationmanager authenticationmanager;
 @autowired
 private redisconnectionfactory connectionfactory;
 
 
 @bean
 public redistokenstore tokenstore() {
  return new redistokenstore(connectionfactory);
 }
 
 
 @override
 public void configure(authorizationserverendpointsconfigurer endpoints) throws exception {
  endpoints
    .authenticationmanager(authenticationmanager)
    .tokenstore(tokenstore());
 }
 
 @override
 public void configure(authorizationserversecurityconfigurer security) throws exception {
  security
    .tokenkeyaccess("permitall()")
    .checktokenaccess("isauthenticated()");
 }
 
 @override
 public void configure(clientdetailsserviceconfigurer clients) throws exception {
  clients.inmemory()
    .withclient("android")
    .scopes("xx") //此处的scopes是无用的,可以随意设置
    .secret("android")
    .authorizedgranttypes("password", "authorization_code", "refresh_token")
   .and()
    .withclient("webapp")
    .scopes("xx")
    .authorizedgranttypes("implicit");
 }
}

2.2 resource服务配置

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@configuration
@enableresourceserver
public class resourceserverconfig extends resourceserverconfigureradapter {
 
 @override
 public void configure(httpsecurity http) throws exception {
  http
    .csrf().disable()
    .exceptionhandling()
    .authenticationentrypoint((request, response, authexception) -> response.senderror(httpservletresponse.sc_unauthorized))
   .and()
    .authorizerequests()
    .anyrequest().authenticated()
   .and()
    .httpbasic();
 }
}
?
1
2
3
4
5
6
7
8
@restcontroller
public class usercontroller {
 
 @getmapping("/user")
 public principal user(principal user){
  return user;
 }
}

2.3 安全配置

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@configuration
public class securityconfig extends websecurityconfigureradapter {
 
 
 
 @bean
 public userdetailsservice userdetailsservice(){
  return new domainuserdetailsservice();
 }
 
 @bean
 public passwordencoder passwordencoder() {
  return new bcryptpasswordencoder();
 }
 
 @override
 protected void configure(authenticationmanagerbuilder auth) throws exception {
  auth
    .userdetailsservice(userdetailsservice())
    .passwordencoder(passwordencoder());
 }
 
 @bean
 public securityevaluationcontextextension securityevaluationcontextextension() {
  return new securityevaluationcontextextension();
 }
 
 //不定义没有password grant_type
 @override
 @bean
 public authenticationmanager authenticationmanagerbean() throws exception {
  return super.authenticationmanagerbean();
 }
 
}

2.4 权限设计

采用用户(sysuser) 角色(sysrole) 权限(sysauthotity)设置,彼此之间的关系是多对多。通过domainuserdetailsservice 加载用户和权限。

2.5 配置

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

2.6 测试数据

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

3.order-service

3.1 resource服务配置

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@configuration
@enableresourceserver
public class resourceserverconfig extends resourceserverconfigureradapter{
 
 @override
 public void configure(httpsecurity http) throws exception {
  http
    .csrf().disable()
    .exceptionhandling()
    .authenticationentrypoint((request, response, authexception) -> response.senderror(httpservletresponse.sc_unauthorized))
   .and()
    .authorizerequests()
    .anyrequest().authenticated()
   .and()
    .httpbasic();
 }
}

3.2 用户信息配置

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

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

3.3 权限测试控制器

具备authorityquery-demo的才能访问,即为admin用户

?
1
2
3
4
5
6
7
8
@restcontroller
public class democontroller {
 @getmapping("/demo")
 @preauthorize("hasauthority('query-demo')")
 public string getdemo(){
  return "good";
 }
}

4 api-gateway

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

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

4.1 关闭csrf并开启oauth2 client支持

?
1
2
3
4
5
6
7
8
9
@configuration
@enableoauth2sso
public class securityconfig extends websecurityconfigureradapter{
 @override
 protected void configure(httpsecurity http) throws exception {
 
  http.csrf().disable();
 }
}

4.2 配置

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

5 演示

5.1 客户端调用

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

admin用户

Spring Cloud下基于OAUTH2认证授权的实现示例

Spring Cloud下基于OAUTH2认证授权的实现示例

Spring Cloud下基于OAUTH2认证授权的实现示例

wyf用户

Spring Cloud下基于OAUTH2认证授权的实现示例

Spring Cloud下基于OAUTH2认证授权的实现示例

Spring Cloud下基于OAUTH2认证授权的实现示例

5.2 api-gateway中的webapp调用

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

6 源码地址

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

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

原文链接:http://www.wisely.top/2017/06/14/spring-cloud-oauth2-zuul/