此篇我将介绍 将zuul 层 和 oauth2 进行分别作为两个服务处理。
进行路由分发和进行关联oauth2服务
org.springframework.cloud
spring-cloud-starter-consul-discovery
org.springframework.cloud
spring-cloud-starter-netflix-zuul
org.springframework.cloud
spring-cloud-starter-oauth2
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
@EnableZuulProxy
@SpringBootApplication
@EnableDiscoveryClient
@Configuration
@EnableWebSecurity
@Order(SecurityProperties.BASIC_AUTH_ORDER - 3)
class SecurityConfig : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity?) {
http!!
.csrf()
.disable()
}
}
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())
}
}
@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()
}
}
}
/**
* 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 {
return@map User(it.account, it.password, it.roles.stream()
.map {
return@map SimpleGrantedAuthority(prefixRoleName(it))
}
.collect(Collectors.toList()))
}.orElseThrow {
return@orElseThrow 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 {
val clientId = accessTokenRepository
.findOneByTokenId(tokenId)
.map { return@map it.clientId }
.orElseThrow {
return@orElseThrow 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