Spring Cloud下基於OAUTH2+ZUUL認證受權的實現

Spring Cloud下基於OAUTH2認證受權的實現

Spring Cloud須要使用OAUTH2來實現多個微服務的統一認證受權,經過向OAUTH服務發送某個類型的grant type進行集中認證和受權,從而得到access_token,而這個token是受其餘微服務信任的,咱們在後續的訪問能夠經過access_token來進行,從而實現了微服務的統一認證受權。android

本示例提供了四大部分:git

 

  • eureka:服務註冊和發現的基本模塊
  • auth:OAUTH2認證受權中心
  • account:普通微服務,用來驗證認證和受權
  • gateway:邊界網關(全部微服務都在它以後)

OAUTH2中的角色:github

  • Resource Server:被受權訪問的資源
  • Authotization Server:OAUTH2認證受權中心
  • Resource Owner: 用戶
  • Client:使用API的客戶端(如Android 、IOS、web app)

Grant Type:web

  • authorization_code:
  • implicit:
  • password:
  • refrsh_token:

1.基礎環境

使用Postgres做爲帳戶存儲,Redis做爲Token存儲,使用docker-compose在服務器上啓動PostgresRedisredis

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.spring

@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)
                .userDetailsService(userDetailsService)//若無,refresh_token會有UserDetailsService is required錯誤
                .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 Serversql

@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();
    }
}
@RestController
public class UserController {

    @GetMapping("/user")
    public Principal user(Principal user){
        return user;
    }
}

2.3 安全配置

@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 加載用戶和權限。docker

2.5 配置

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


2.6 測試數據

data.sql裏初始化了兩個用戶admin->ROLE_ADMIN->query_demo,wyf->ROLE_USERapi

3.account

3.1 Resource服務配置

@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 用戶信息配置

account是一個簡單的微服務,使用auth-server進行認證受權,在它的配置文件指定用戶信息在auth-server的地址便可:安全

security:
  oauth2:
    resource:
      id: order-service
      user-info-uri: http://localhost:8080/uaa/user
      prefer-token-info: false

3.3 權限測試控制器

具有authorityquery-demo的才能訪問,即爲admin用戶

@RestController
public class DemoController {
    @GetMapping("/demo")
    @PreAuthorize("hasAuthority('query-demo')")
    public String getDemo(){
        return "good";
    }
}

4 api-gateway

api-gateway在本例中有2個做用:

  • 自己做爲一個client,使用implicit

  • 做爲外部app訪問的方向代理

4.1 關閉csrf並開啓Oauth2 client支持

@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter{
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable();

    }

}

4.2 配置

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

4.3 服務之間互調

feign client沒有將access token放入請求頭裏,需定義一個OAuth2FeignRequestInterceptor的bean:

5 演示

5.1 客戶端調用

使用Postmanhttp://localhost:8080/uaa/oauth/token發送請求得到access_token(admin用戶的如7f9b54d4-fd25-4a2c-a848-ddf8f119230b)

  • admin用戶



  • wyf用戶



5.2 api-gateway中的webapp調用

暫時沒有作測試,下次補充。

 

 代碼開源

  代碼已經開源、託管到個人碼雲:

               https://gitee.com/senhelpa-vivo/spring-cloud.git

相關文章
相關標籤/搜索