1. 程式人生 > >SpringCloud--zuul和oauth2

SpringCloud--zuul和oauth2

文章目錄

此篇我將介紹 將zuul 層 和 oauth2 進行分別作為兩個服務處理。

zuul

進行路由分發和進行關聯oauth2服務

  • Pom需要的依賴
		 <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
  • 配置檔案
server:
  port: 10000
  servlet:
    session:
      cookie:
        name: UISESSION
spring:
  cloud:
    consul:
      host: localhost
      #  Consul監聽埠8500
      port: 8500
      discovery:
        # 配置服務註冊到Consul上
        register: true
        # 配置服務健康檢測地址  供Consul 呼叫
        health-check-path: /api/health
        # consul 健康檢測頻率
        health-check-interval: 15s
        # 配置註冊到consul 服務的id
        instance-id: ${spring.application.name}:${server.port}
        enabled: true
        service-name: ${spring.application.name}
  application:
    name: ecology-getway
  thymeleaf:
    cache: true
zuul:
  host:
    connect-timeout-millis: 10000
    socket-timeout-millis: 60000
  routes:
    ratelimit-api:
      path: /ratelimit-api/**
      serviceId: service-client
      sensitiveHeaders: "*"
    login:
      path: /login/**
      sensitiveHeaders: "*"
      serviceId: server-login
    auth:
      path: /uaa/**
      serviceId: ecology-oauth2
      strip-prefix: true
      custom-sensitive-headers: true
    user:
      path: /user/**
      serviceId: service-account-center
  ratelimit:
    key-prefix: ratelimit-api
    # 啟動限流服務
    enabled: true
    behind-proxy: true
    default-policy:
      # 請求數量
      limit: 100
      # 請求總時間
      quota: 20
      # 統計視窗重新整理時間
      refresh-interval: 60
      # 限流型別
      type: url
  ignored-services: '*'
  add-proxy-headers: true
  retryable: true
  sensitive-headers:
security:
  oauth2:
    client:
      access-token-uri: http://localhost:${server.port}/uaa/oauth/token
      user-authorization-uri: http://localhost:${server.port}/uaa/oauth/authorize
      client-id: open-api
    resource:
      user-info-uri: http://localhost:${server.port}/uaa/user
      prefer-token-info: false
  • springboot入口處進行配置 開啟zuul
@EnableZuulProxy
@SpringBootApplication
@EnableDiscoveryClient
  • 增加我們的websecurity的配置
@Configuration
@EnableWebSecurity
@Order(SecurityProperties.BASIC_AUTH_ORDER - 3)
class SecurityConfig : WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity?) {
        http!!
                .csrf()
                .disable()
    }


}

oauth2

Oauth2的服務模組

資源配置

根據自己的情況進行修改,是否部分資源可以不需要授權進行請求

@Configuration
@EnableResourceServer
class ResourceServerConfig : ResourceServerConfigurerAdapter(){

    override fun configure(http: HttpSecurity?) {
        http!!
                .anonymous()
                .disable()
                .requestMatchers()
                .antMatchers("/api/**")
                .and()
                .authorizeRequests()
                .antMatchers("/api/**")
                .fullyAuthenticated()
                .and()
                .exceptionHandling()
                .accessDeniedHandler(OAuth2AccessDeniedHandler())
    }
}

授權服務

這裡採用了redis進行Token的儲存。

@Configuration
@EnableAuthorizationServer
class AuthorizationServerConfig : AuthorizationServerConfigurerAdapter(){

    @Autowired
    private lateinit var oAuth2DatabaseClientDetailsService: OAuth2DatabaseClientDetailsService

    @Autowired
    private lateinit var authenticationManager: AuthenticationManager

    @Autowired
    private lateinit var redisFactory: RedisConnectionFactory

    @Bean
    fun tokenStore(): TokenStore {
       return RedisTokenStore(redisFactory)
    }

    override fun configure(security: AuthorizationServerSecurityConfigurer?) {
        security!!
                .tokenKeyAccess("permitAll()")
    }

    override fun configure(clients: ClientDetailsServiceConfigurer?) {
        clients!!.withClientDetails(oAuth2DatabaseClientDetailsService)
    }

    override fun configure(endpoints: AuthorizationServerEndpointsConfigurer?) {
        endpoints!!
                .authenticationManager(authenticationManager)
                .tokenStore(tokenStore())
    }
}

web策略

@Configuration
class WebSecurityConfig : WebSecurityConfigurerAdapter(){

    private var log: Logger = LoggerFactory.getLogger(WebSecurityConfig::class.java)

    @Bean
    fun userDetail():UserDetailsService {
        return DataBaseUserDetailsService()
    }


    @Bean
    fun passwordEncoder(): PasswordEncoder {
        return BCryptPasswordEncoder()
    }

    override fun configure(auth: AuthenticationManagerBuilder?) {
        auth!!
                .userDetailsService(userDetail())
                .passwordEncoder(passwordEncoder())
    }

    @Bean
    override fun authenticationManagerBean(): AuthenticationManager {
        return super.authenticationManagerBean()
    }


    override fun configure(http: HttpSecurity?) {
        http!!
                .authorizeRequests()
                .antMatchers("/", "/auth/**", "/api/health", "/oauth/**", "/default/**", "/login","/user")
                .permitAll()
                .anyRequest()
                .authenticated()
    }

    override fun configure(web: WebSecurity?) {
        web!!.ignoring().antMatchers("/resources/**")
    }

    /**
     * describe: 返回對應的使用者名稱
     * author 候帥
     * date 2018/8/19 上午9:28
     * @return 使用者名稱
     */
    @Bean
    fun auditorAwareBean(): String {
        val authentication = SecurityContextHolder.getContext().authentication
        if (authentication == null || AuthenticationTrustResolverImpl().isAnonymous(authentication))
            return "@SYSTEM"
        val principal = authentication.principal
        return when (principal) {
            is String -> principal
            is UserDetails -> principal.username
            else -> principal.toString()
        }
    }
}

使用者UserDetails服務


/**
 * FileName: DataBaseUserDetailsService
 * author:   候帥
 * data:     18/08/2018 07:40
 * Description: 自定義使用者身份獲取
 * History:
 */
@Service
class DataBaseUserDetailsService :UserDetailsService {

    private val log: Logger = LoggerFactory.getLogger(DataBaseUserDetailsService::class.java)

    companion object {
        private const val ROLE_PREFIX: String = "ROLE_"
    }

    @Autowired
    private lateinit var userFeign: UserFeign

    @Autowired
    private lateinit var accessTokenRepository: AccessTokenRepository

    @Autowired
    private lateinit var clientDetailsRepository: ClientDetailsRepository

    override fun loadUserByUsername(username: String?): UserDetails {

       return Optional.ofNullable(userFeign.findUserInfo(username!!))
                .map {
                    [email protected] User(it.account, it.password, it.roles.stream()
                            .map {
                                [email protected] SimpleGrantedAuthority(prefixRoleName(it))
                            }
                            .collect(Collectors.toList()))
                }.orElseThrow {
                    [email protected] UsernameNotFoundException("User $username was not found in the database")
                }
    }

    /**
     * describe: 獲取客戶端的Token 內容
     * author 候帥
     * date 2018/8/18 下午10:21
     * @param tokenId token對應的id
     * @return   返回間隔時間
     */
    fun loadClientByToken(tokenId: String): Triple<Long, String, Long> {
        val clientId = accessTokenRepository
                .findOneByTokenId(tokenId)
                .map { [email protected] it.clientId }
                .orElseThrow {
                    [email protected] UsernameNotFoundException("Token $tokenId was not found in the database")
                }
        val details = clientDetailsRepository.findOneByClientId(clientId).get()
        val clientLimit = details.clientLimit
        return Triple(clientLimit.intervalInMills, clientId, clientLimit.limits)
    }

    /**
     * describe: 將未有字首的內容 新增
     * author 候帥
     * date 2018/8/18 下午10:20
     * @param roleName 角色名
     * @return 返回過濾的角色名
     */
    fun prefixRoleName(roleName: String): String {
        if (!StringUtils.isEmpty(roleName) && !roleName.startsWith(ROLE_PREFIX))
            return ROLE_PREFIX + roleName
        return roleName
    }
}

配置檔案

server:
  port: 10010
spring:
  cloud:
    consul:
      host: localhost
      #  Consul監聽埠8500
      port: 8500
      discovery:
        # 配置服務註冊到Consul上
        register: true
        # 配置服務健康檢測地址  供Consul 呼叫
        health-check-path: /api/health
        # consul 健康檢測頻率
        health-check-interval: 15s
        # 配置註冊到consul 服務的id
        instance-id: ${spring.application.name}:${server.port}
        enabled: true
        service-name: ${spring.application.name}
  application:
    name: ecology-oauth2
  redis:
    # 設定資料庫索引
    database: 0
    host: localhost
    port: 6379
    password:
    jedis:
      pool:
        max-active: 10
        max-idle: 10
        min-idle: 0
        max-wait: -1ms
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/andy?useSSL=false
    username: root
    password: root
    dbcp2:
      # 初始化連線數
      initial-size: 5
      # 最小連線數
      min-idle: 5
      # 最大連線數
      max-idle: 20
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
  profiles:
    include: default-user-and-roles_route
ribbon:
  eager-load:
    enabled: false