当前位置:   article > 正文

芋道 spring security oauth2 入门_SpringCloud Alibaba微服务实战十四 - Gateway集成Oauth2.0

芋道gateway

7d81bd26ca9aec53db2af5fc60933f79.png

导读:上篇文章我们已经抽取出了单独的认证服务,本章主要内容是让SpringCloud Gateway 集成Oauth2。

概念部分

a5fec658b6873c8bd0e58e0f7b598a08.png

在网关集成Oauth2.0后,我们的流程架构如上。主要逻辑如下:
1、客户端应用通过api网关请求认证服务器获取access_token http://localhost:8090/auth-service/oauth/token2、认证服务器返回access_token

  1. {
  2. "access_token": "f938d0c1-9633-460d-acdd-f0693a6b5f4c",
  3. "token_type": "bearer",
  4. "refresh_token": "4baea735-3c0d-4dfd-b826-91c6772a0962",
  5. "expires_in": 43199,
  6. "scope": "web"
  7. }

3、客户端携带access_token通过API网关访问后端服务

002c47f3024bb0c6f78198c5fb72cfbd.png

4、API网关收到access_token后通过 AuthenticationWebFilter 对access_token认证

5、API网关转发后端请求,后端服务请求Oauth2认证服务器获取当前用户

在前面文章中我们搭建好了单独的Oauth2认证授权服务,基本功能框架都实现了,这次主要是来实现第四条,SpringCloud 整合 Oauth2 后如何进行access_token过滤校验。

代码示例

引入组件

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-security</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.springframework.security</groupId>
  7. <artifactId>spring-security-oauth2-resource-server</artifactId>
  8. </dependency>
  9. <dependency>
  10. <groupId>org.springframework.cloud</groupId>
  11. <artifactId>spring-cloud-starter-oauth2</artifactId>
  12. </dependency>
  13. <dependency>
  14. <groupId>org.springframework.boot</groupId>
  15. <artifactId>spring-boot-starter-jdbc</artifactId>
  16. </dependency>
  17. <dependency>
  18. <groupId>mysql</groupId>
  19. <artifactId>mysql-connector-java</artifactId>
  20. </dependency>

主要引入跟oauth2相关的jar包,这里还需要引入数据库相关的jar包,因为我们的token是存在数据库中,要想在网关层校验token的有效性必须先从数据库取出token。

bootstrap.yml 配置修改

  1. spring:
  2. application:
  3. name: cloud-gateway
  4. datasource:
  5. type: com.zaxxer.hikari.HikariDataSource
  6. url: jdbc:mysql://xx.0.xx.xx:3306/oauth2_config?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false
  7. username: xxxxx
  8. password: xxxxxxx
  9. driver-class-name: com.mysql.jdbc.Driver

主要配置oauth2的数据库连接地址

自定义认证接口管理类

在webFlux环境下通过实现 ReactiveAuthenticationManager 接口 自定义认证接口管理,由于我们的token是存在jdbc中所以命名上就叫ReactiveJdbcAuthenticationManager

  1. @Slf4j
  2. public class ReactiveJdbcAuthenticationManager implements ReactiveAuthenticationManager {
  3. private TokenStore tokenStore;
  4. public JdbcAuthenticationManager(TokenStore tokenStore){
  5. this.tokenStore = tokenStore;
  6. }
  7. @Override
  8. public Mono<Authentication> authenticate(Authentication authentication) {
  9. return Mono.justOrEmpty(authentication)
  10. .filter(a -> a instanceof BearerTokenAuthenticationToken)
  11. .cast(BearerTokenAuthenticationToken.class)
  12. .map(BearerTokenAuthenticationToken::getToken)
  13. .flatMap((accessToken ->{
  14. log.info("accessToken is :{}",accessToken);
  15. OAuth2AccessToken oAuth2AccessToken = this.tokenStore.readAccessToken(accessToken);
  16. //根据access_token从数据库获取不到OAuth2AccessToken
  17. if(oAuth2AccessToken == null){
  18. return Mono.error(new InvalidTokenException("invalid access token,please check"));
  19. }else if(oAuth2AccessToken.isExpired()){
  20. return Mono.error(new InvalidTokenException("access token has expired,please reacquire token"));
  21. }
  22. OAuth2Authentication oAuth2Authentication =this.tokenStore.readAuthentication(accessToken);
  23. if(oAuth2Authentication == null){
  24. return Mono.error(new InvalidTokenException("Access Token 无效!"));
  25. }else {
  26. return Mono.just(oAuth2Authentication);
  27. }
  28. })).cast(Authentication.class);
  29. }
  30. }

网关层的安全配置

  1. @Configuration
  2. public class SecurityConfig {
  3. private static final String MAX_AGE = "18000L";
  4. @Autowired
  5. private DataSource dataSource;
  6. @Autowired
  7. private AccessManager accessManager;
  8. /**
  9. * 跨域配置
  10. */
  11. public WebFilter corsFilter() {
  12. return (ServerWebExchange ctx, WebFilterChain chain) -> {
  13. ServerHttpRequest request = ctx.getRequest();
  14. if (CorsUtils.isCorsRequest(request)) {
  15. HttpHeaders requestHeaders = request.getHeaders();
  16. ServerHttpResponse response = ctx.getResponse();
  17. HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();
  18. HttpHeaders headers = response.getHeaders();
  19. headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());
  20. headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders());
  21. if (requestMethod != null) {
  22. headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());
  23. }
  24. headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
  25. headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
  26. headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE);
  27. if (request.getMethod() == HttpMethod.OPTIONS) {
  28. response.setStatusCode(HttpStatus.OK);
  29. return Mono.empty();
  30. }
  31. }
  32. return chain.filter(ctx);
  33. };
  34. }
  35. @Bean
  36. SecurityWebFilterChain webFluxSecurityFilterChain(ServerHttpSecurity http) throws Exception{
  37. //token管理器
  38. ReactiveAuthenticationManager tokenAuthenticationManager = new ReactiveJdbcAuthenticationManager(new JdbcTokenStore(dataSource));
  39. //认证过滤器
  40. AuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(tokenAuthenticationManager);
  41. authenticationWebFilter.setServerAuthenticationConverter(new ServerBearerTokenAuthenticationConverter());
  42. http
  43. .httpBasic().disable()
  44. .csrf().disable()
  45. .authorizeExchange()
  46. .pathMatchers(HttpMethod.OPTIONS).permitAll()
  47. .anyExchange().access(accessManager)
  48. .and()
  49. // 跨域过滤器
  50. .addFilterAt(corsFilter(), SecurityWebFiltersOrder.CORS)
  51. //oauth2认证过滤器
  52. .addFilterAt(authenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION);
  53. return http.build();
  54. }
  55. }

这个类是SpringCloug Gateway 与 Oauth2整合的关键,通过构建认证过滤器 AuthenticationWebFilter 完成Oauth2.0的token校验。AuthenticationWebFilter 通过我们自定义的 ReactiveJdbcAuthenticationManager 完成token校验。我们在这里还加入了CORS过滤器,以及权限管理器 AccessManager

权限管理器

  1. @Slf4j
  2. @Component
  3. public class AccessManager implements ReactiveAuthorizationManager<AuthorizationContext> {
  4. private Set<String> permitAll = new ConcurrentHashSet<>();
  5. private static final AntPathMatcher antPathMatcher = new AntPathMatcher();
  6. public AccessManager (){
  7. permitAll.add("/");
  8. permitAll.add("/error");
  9. permitAll.add("/favicon.ico");
  10. permitAll.add("/**/v2/api-docs/**");
  11. permitAll.add("/**/swagger-resources/**");
  12. permitAll.add("/webjars/**");
  13. permitAll.add("/doc.html");
  14. permitAll.add("/swagger-ui.html");
  15. permitAll.add("/**/oauth/**");
  16. permitAll.add("/**/current/get");
  17. }
  18. /**
  19. * 实现权限验证判断
  20. */
  21. @Override
  22. public Mono<AuthorizationDecision> check(Mono<Authentication> authenticationMono, AuthorizationContext authorizationContext) {
  23. ServerWebExchange exchange = authorizationContext.getExchange();
  24. //请求资源
  25. String requestPath = exchange.getRequest().getURI().getPath();
  26. // 是否直接放行
  27. if (permitAll(requestPath)) {
  28. return Mono.just(new AuthorizationDecision(true));
  29. }
  30. return authenticationMono.map(auth -> {
  31. return new AuthorizationDecision(checkAuthorities(exchange, auth, requestPath));
  32. }).defaultIfEmpty(new AuthorizationDecision(false));
  33. }
  34. /**
  35. * 校验是否属于静态资源
  36. * @param requestPath 请求路径
  37. * @return
  38. */
  39. private boolean permitAll(String requestPath) {
  40. return permitAll.stream()
  41. .filter(r -> antPathMatcher.match(r, requestPath)).findFirst().isPresent();
  42. }
  43. //权限校验
  44. private boolean checkAuthorities(ServerWebExchange exchange, Authentication auth, String requestPath) {
  45. if(auth instanceof OAuth2Authentication){
  46. OAuth2Authentication athentication = (OAuth2Authentication) auth;
  47. String clientId = athentication.getOAuth2Request().getClientId();
  48. log.info("clientId is {}",clientId);
  49. }
  50. Object principal = auth.getPrincipal();
  51. log.info("用户信息:{}",principal.toString());
  52. return true;
  53. }
  54. }

主要是过滤掉静态资源,将来一些接口权限校验也可以放在这里。

测试

  • 通过网关调用auth-service获取 access_token

c3aefcabb635d47884178e17db13b68c.png
  • 在Header上添加认证访问后端服务

35ef882a60158c2f21ab82c65561501e.png
  • 网关过滤器进行token校验

d19be7dff568f820d0789b8e0f781f71.png
  • 权限管理器校验

4289efc7b6825c3336dce40b0e4b397b.png
  • 去认证服务器校验当前用户

e4c0c660e8b7b815667109c93fbe2e48.png
  • 返回正常结果

68659e0e28c5c4367f57c5d2f24062f7.png
  • 故意写错access_token,返回错误响应

1cd8394ff85cbc65c4c4b87d26b7ab5c.png
  • 请求头上去掉access_token,直接返回401 Unauthorized

c54ee5e246b87bb642b5742df6720629.png

总结

通过以上几步我们将SpringCloud Gateway整合好了Oauth2.0,这样我们整个项目也基本完成了,后面几期再来对项目进行优化,欢迎持续关注。

好了,各位朋友们,本期的内容到此就全部结束啦,能看到这里的同学都是优秀的同学,下一个升职加薪的就是你了!

如果觉得这篇文章对你有所帮助的话请扫描下面二维码加个关注。"转发" 加 "在看",养成好习惯!咱们下期再见!

aecb60a7947a5c07a2387c6bdaa6f4b6.png

系列文章

SpringCloud 标签 - JAVA日知录​www.javadaily.cn
6d9f2c506a8d81002756672325339cdb.png

http://weixin.qq.com/r/WhyIkNPE-46ArZfz90lI (二维码自动识别)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/84480
推荐阅读
相关标签
  

闽ICP备14008679号