版本聲明
Springboot,springcloud,spring secutity,spring ouath2都需要有明確的版本聲明,對於不同版本的類庫,實現上也有很大的區別,不同版本的授權是不能通用的。
項目定義
- 網關服務 gateway
- 授權服務 oauth,uaa
- 用戶服務 system-service
- 其它微服務 others-service
授權流轉方式
- 用戶調用gateway的登陸接口
- gateway里進行參數組織,調用oauth的頒發token接口
- 用戶拿到token之后,帶着token去訪問資源服務的接口
版本定義
springboot+springcloud
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/>
</parent>
<properties>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
<spring-cloud-alibaba.version>2.0.2.RELEASE</spring-cloud-alibaba.version>
<spring-cloud-starter-oauth2.version>2.0.0.RELEASE</spring-cloud-starter-oauth2.version>
<spring-security-oauth2.version>2.3.3.RELEASE</spring-security-oauth2.version>
<spring-boot-starter-security.version>2.0.0.RELEASE</spring-boot-starter-security.version>
</properties>
<dependencyManagement>
<dependencies>
<!--spring cloud 版本-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
gateway,oauth,system等服務添加引用
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>${spring-boot-starter-security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>${spring-security-oauth2.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>${spring-cloud-starter-oauth2.version}</version>
</dependency>
gateway添加bean
@EnableWebFluxSecurity
public class GatewaySecurityConfig {
@Bean
SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity serverHttpSecurity)
throws Exception {
serverHttpSecurity
.csrf().disable();
serverHttpSecurity.cors();
return serverHttpSecurity.build();
}
}
ouath添加bean
AuthorizationServerConfig繼承AuthorizationServerConfigurerAdapter 用來配置客戶端詳情服務(ClientDetailsService),客戶端詳情信息在這里進行初始化,你能夠把客戶端詳情信息寫死在這里或者是通過數據庫來存儲調取詳情信息。
ResourceServerConfig它上面添加注解@EnableResourceServer幫我們加入了
org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter,該filter幫我們從request里解析出access_token並通過org.springframework.security.oauth2.provider.token.DefaultTokenServices根據access_token和認證服務器配置里的TokenStore從redis或者jwt里解析出用戶注意認證中心的@EnableResourceServer和別的微服務里的@EnableResourceServer有些不同別的微服務是通過org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices來獲取用戶的.
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatcher(new OAuth2RequestedMatcher()).authorizeRequests()
.antMatchers(PermitAllUrl.permitAllUrl()).permitAll() // 放開權限的url
.anyRequest().authenticated();
}
/**
* 判斷來源請求是否包含oauth2授權信息<br>
* url參數中含有access_token,或者header里有Authorization
*/
private static class OAuth2RequestedMatcher implements RequestMatcher {
@Override
public boolean matches(HttpServletRequest request) {
// 請求參數中包含access_token參數
if (request.getParameter(OAuth2AccessToken.ACCESS_TOKEN) != null) {
return true;
}
// 頭部的Authorization值以Bearer開頭
String auth = request.getHeader("Authorization");
if (auth != null) {
return auth.startsWith(OAuth2AccessToken.BEARER_TYPE);
}
return false;
}
}
}
SecurityConfig,這個bean主要設置用戶獲取接口和加密規則
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
public UserDetailsService userDetailsService;
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Value("${pkulaw.token.redis}")
private Boolean tokenRedis;
@Value("${pkulaw.tokenExpireTime}")
private Integer tokenExpireTime;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
/**
* 認證管理
*
* @return 認證管理對象
* @throws Exception 認證異常信息
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* http安全配置
*
* @param http http安全對象
* @throws Exception http安全異常信息
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(PermitAllUrl.permitAllUrl()).permitAll() // 放開權限的url
.anyRequest().authenticated().and()
.httpBasic().and().csrf().disable();
}
UserDetailServiceImpl用來獲取用戶信息,通過通過feign獲取system里的接口
@Service("userDetailsService")
public class UserDetailServiceImpl implements UserDetailsService {
@Autowired
StringRedisTemplate redisTemplate;
@Autowired
ObjectMapper objectMapper;
@Autowired
private UserClient userClient;
@Autowired
private RedisUtil redisUtil;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
String flagKey = "loginFailFlag:" + username;
String value = redisTemplate.opsForValue().get(flagKey);
Long timeRest = redisTemplate.getExpire(flagKey, TimeUnit.MINUTES);
if (StrUtil.isNotBlank(value)) {
//超過限制次數
System.out.println("登錄錯誤次數超過限制,請" + timeRest + "分鍾后再試");
throw new PkuLawException("登錄錯誤次數超過限制,請" + timeRest + "分鍾后再試");
}
User user = userClient.findByUsername(username);
//持久化到redis
redisUtil.set(RedisConstant.USER + username, user);
return new SecurityUserDetails(user);
}
}
system添加bean
system也是資源服務的一種,主要提供用戶服務,每個資源服務都有一個ResourceServerConfig的bean,用來規定你的資源開放策略。
/**
* 資源服務配置
*/
@EnableResourceServer
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
super.configure(resources);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().exceptionHandling()
.authenticationEntryPoint(
(request, response, authException) -> response.sendError(
HttpServletResponse.SC_UNAUTHORIZED))
.and().authorizeRequests()
.antMatchers(PermitAllUrl
.permitAllUrl("/company/register","/druid/**","/file/view/**","/**/users-anon/**")).permitAll() // 放開權限的url
.anyRequest().authenticated()
.and().httpBasic()
.and().addFilterBefore(new BeforeTokenFilter(), AbstractPreAuthenticatedProcessingFilter.class)
;
http.headers().frameOptions().sameOrigin();
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
資源服務的配置
通過在yml里對oauth2的配置,來獲取當前oauth里的登錄信息,並把登陸狀態寫到自己服務的HTTP請求里,這個過程是oauth框架幫我們實現的。
security:
oauth2:
resource:
user-info-uri: http://localhost:6660/pkulaw/oauth/user-me #授權服務的獲取當前用戶接口,它的返回值是Authentication類型,它會把返回值寫到當前服務的請求頭里
prefer-token-info: false
這篇文章主要記錄了oauth2搭建的過程。
