在网关集成Oauth2.0后,我们的流程架构如上。主要逻辑如下:
1、客户端应用通过api网关请求认证服务器获取access_token http://localhost:8090/auth-service/oauth/token
2、认证服务器返回access_token
{
“access_token”: “f938d0c1-9633-460d-acdd-f0693a6b5f4c”,
“token_type”: “bearer”,
“refresh_token”: “4baea735-3c0d-4dfd-b826-91c6772a0962”,
“expires_in”: 43199,
“scope”: “web”
}
3、客户端携带access_token通过API网关访问后端服务
4、API网关收到access_token后通过 AuthenticationWebFilter
对access_token认证
5、API网关转发后端请求,后端服务请求Oauth2认证服务器获取当前用户
在前面文章中我们搭建好了单独的Oauth2认证授权服务,基本功能框架都实现了,这次主要是来实现第四条,SpringCloud 整合 Oauth2 后如何进行access_token过滤校验。
引入组件
org.springframework.boot
spring-boot-starter-security
org.springframework.security
spring-security-oauth2-resource-server
org.springframework.cloud
spring-cloud-starter-oauth2
org.springframework.boot
spring-boot-starter-jdbc
mysql
mysql-connector-java
主要引入跟oauth2相关的jar包,这里还需要引入数据库相关的jar包,因为我们的token是存在数据库中,要想在网关层校验token的有效性必须先从数据库取出token。
bootstrap.yml 配置修改
spring:
application:
name: cloud-gateway
datasource:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://xx.0.xx.xx:3306/oauth2_config?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false
username: xxxxx
password: xxxxxxx
driver-class-name: com.mysql.jdbc.Driver
主要配置oauth2的数据库连接地址
自定义认证接口管理类
在webFlux环境下通过实现 ReactiveAuthenticationManager
接口 自定义认证接口管理,由于我们的token是存在jdbc中所以命名上就叫ReactiveJdbcAuthenticationManager
@Slf4j
public class ReactiveJdbcAuthenticationManager implements ReactiveAuthenticationManager {
private TokenStore tokenStore;
public JdbcAuthenticationManager(TokenStore tokenStore){
this.tokenStore = tokenStore;
}
@Override
public Mono authenticate(Authentication authentication) {
return Mono.justOrEmpty(authentication)
.filter(a -> a instanceof BearerTokenAuthenticationToken)
.cast(BearerTokenAuthenticationToken.class)
.map(BearerTokenAuthenticationToken::getToken)
.flatMap((accessToken ->{
log.info(“accessToken is :{}”,accessToken);
OAuth2AccessToken oAuth2AccessToken = this.tokenStore.readAccessToken(accessToken);
//根据access_token从数据库获取不到OAuth2AccessToken
if(oAuth2AccessToken == null){
return Mono.error(new InvalidTokenException(“invalid access token,please check”));
}else if(oAuth2AccessToken.isExpired()){
return Mono.error(new InvalidTokenException(“access token has expired,please reacquire token”));
}
OAuth2Authentication oAuth2Authentication =this.tokenStore.readAuthentication(accessToken);
if(oAuth2Authentication == null){
return Mono.error(new InvalidTokenException(“Access Token 无效!”));
}else {
return Mono.just(oAuth2Authentication);
}
})).cast(Authentication.class);
}
}
网关层的安全配置
@Configuration
public class SecurityConfig {
private static final String MAX_AGE = “18000L”;
@Autowired
private DataSource dataSource;
@Autowired
private AccessManager accessManager;
/**
- 跨域配置
*/
public WebFilter corsFilter() {
return (ServerWebExchange ctx, WebFilterChain chain) -> {
ServerHttpRequest request = ctx.getRequest();
if (CorsUtils.isCorsRequest(request)) {
HttpHeaders requestHeaders = request.getHeaders();
ServerHttpResponse response = ctx.getResponse();
HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();
HttpHeaders headers = response.getHeaders();
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());
headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders());
if (requestMethod != null) {
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());
}
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, “true”);
headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, “*”);
headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE);
if (request.getMethod() == HttpMethod.OPTIONS) {
response.setStatusCode(HttpStatus.OK);
return Mono.empty();
}
}
return chain.filter(ctx);
};
}
@Bean
SecurityWebFilterChain webFluxSecurityFilterChain(ServerHttpSecurity http) throws Exception{
//token管理器
ReactiveAuthenticationManager tokenAuthenticationManager = new ReactiveJdbcAuthenticationManager(new JdbcTokenStore(dataSource));
//认证过滤器
AuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(tokenAuthenticationManager);
authenticationWebFilter.setServerAuthenticationConverter(new ServerBearerTokenAuthenticationConverter());
http
.httpBasic().disable()
.csrf().disable()
.authorizeExchange()
.pathMatchers(HttpMethod.OPTIONS).permitAll()
.anyExchange().access(accessManager)
.and()
// 跨域过滤器
.addFilterAt(corsFilter(), SecurityWebFiltersOrder.CORS)
//oauth2认证过滤器
.addFilterAt(authenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION);
return http.build();
}
}
这个类是SpringCloug Gateway 与 Oauth2整合的关键,通过构建认证过滤器 AuthenticationWebFilter
完成Oauth2.0的token校验。
AuthenticationWebFilter
通过我们自定义的 ReactiveJdbcAuthenticationManager
完成token校验。
我们在这里还加入了CORS
过滤器,以及权限管理器 AccessManager
权限管理器
@Slf4j
@Component
public class AccessManager implements ReactiveAuthorizationManager {
private Set permitAll = new ConcurrentHashSet<>();
private static final AntPathMatcher antPathMatcher = new AntPathMatcher();
public AccessManager (){
permitAll.add(“/”);
permitAll.add(“/error”);
permitAll.add(“/favicon.ico”);
permitAll.add(“//v2/api-docs/”);
permitAll.add(“//swagger-resources/”);
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
的负担。**
[外链图片转存中…(img-srjuYiXS-1715079493021)]
[外链图片转存中…(img-a9CCgLbv-1715079493022)]
[外链图片转存中…(img-bFCcRty5-1715079493022)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!