irpas技术客

SpringCloud Gateway 集成 oauth2 实现统一认证授权_03_Gblfy_Blog_springcloudgateway权限验证

大大的周 1872

文章目录 一、网关搭建1. 引入依赖2. 配置文件3. 增加权限管理器4. 自定义认证接口管理类5. 增加网关层的安全配置6. 搭建授权认证中心 二、搭建产品服务2.1. 创建boot项目2.2. 引入依赖2.3. controller2.4. 启动类2.5. 配置 四、测试验证4.1. 启动nacos4.2. 启动认证中心4.3. 启动产品服务4.3. 请求认证授权中心4.4. 网关请求产品模块4.5. 获取token4.6. 携带token请求产品服务4.7. 直接请求产品服务4.8. 请求结果比对 五、总结

一、网关搭建 1. 引入依赖 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <spring.cloud-version>Hoxton.SR9</spring.cloud-version> </properties> <dependencies> <!--安全认证框架--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!--security-oauth2整合--> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-oauth2-resource-server</artifactId> </dependency> <!--oauth2--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--网关--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> </dependencies> <dependencyManagement> <!--https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E--> <dependencies> <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> 2. 配置文件 server: port: 8081 spring: cloud: gateway: routes: - id: product uri: http://localhost:9000 predicates: - Host=product.gblfy.com** - id: auth uri: http://localhost:5000 predicates: - Path=/oauth/token - id: skill uri: http://localhost:13000 predicates: - Path=/skill datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/auth-serv?characterEncoding=UTF-8&serverTimezone=GMT%2B8 username: root password: 123456 3. 增加权限管理器 package com.gblfy.gatewayserv.config; import lombok.extern.slf4j.Slf4j; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.ReactiveAuthorizationManager; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.web.server.authorization.AuthorizationContext; import org.springframework.stereotype.Component; import org.springframework.util.AntPathMatcher; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.util.Set; import java.util.concurrent.ConcurrentSkipListSet; @Slf4j @Component public class AccessManager implements ReactiveAuthorizationManager<AuthorizationContext> { private Set<String> permitAll = new ConcurrentSkipListSet<>(); 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/**"); permitAll.add("/webjars/**"); permitAll.add("/doc.html"); permitAll.add("/swagger-ui.html"); permitAll.add("/**/oauth/**"); permitAll.add("/**/current/get"); } /** * 实现权限验证判断 */ @Override public Mono<AuthorizationDecision> check(Mono<Authentication> authenticationMono, AuthorizationContext authorizationContext) { ServerWebExchange exchange = authorizationContext.getExchange(); //请求资源 String requestPath = exchange.getRequest().getURI().getPath(); // 是否直接放行 if (permitAll(requestPath)) { return Mono.just(new AuthorizationDecision(true)); } return authenticationMono.map(auth -> { return new AuthorizationDecision(checkAuthorities(exchange, auth, requestPath)); }).defaultIfEmpty(new AuthorizationDecision(false)); } /** * 校验是否属于静态资源 * * @param requestPath 请求路径 * @return */ private boolean permitAll(String requestPath) { return permitAll.stream() .filter(r -> antPathMatcher.match(r, requestPath)).findFirst().isPresent(); } //权限校验 private boolean checkAuthorities(ServerWebExchange exchange, Authentication auth, String requestPath) { if (auth instanceof OAuth2Authentication) { OAuth2Authentication athentication = (OAuth2Authentication) auth; String clientId = athentication.getOAuth2Request().getClientId(); log.info("clientId is {}", clientId); } Object principal = auth.getPrincipal(); log.info("用户信息:{}", principal.toString()); return true; } } 4. 自定义认证接口管理类 package com.gblfy.gatewayserv.config; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.authentication.ReactiveAuthenticationManager; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken; import reactor.core.publisher.Mono; public class ReactiveJdbcAuthenticationManager implements ReactiveAuthenticationManager { Logger logger= LoggerFactory.getLogger(ReactiveJdbcAuthenticationManager.class); private TokenStore tokenStore; public ReactiveJdbcAuthenticationManager(TokenStore tokenStore){ this.tokenStore = tokenStore; } @Override public Mono<Authentication> authenticate(Authentication authentication) { return Mono.justOrEmpty(authentication) .filter(a -> a instanceof BearerTokenAuthenticationToken) .cast(BearerTokenAuthenticationToken.class) .map(BearerTokenAuthenticationToken::getToken) .flatMap((accessToken ->{ logger.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); } } 5. 增加网关层的安全配置 package com.gblfy.gatewayserv.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.ReactiveAuthenticationManager; import org.springframework.security.config.web.server.SecurityWebFiltersOrder; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore; import org.springframework.security.oauth2.server.resource.web.server.ServerBearerTokenAuthenticationConverter; import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.web.server.authentication.AuthenticationWebFilter; import javax.sql.DataSource; @Configuration public class SecurityConfig { private static final String MAX_AGE = "18000L"; @Autowired private DataSource dataSource; @Autowired private AccessManager accessManager; @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() //oauth2认证过滤器 .addFilterAt(authenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION); return http.build(); } }

这个类是SpringCloug Gateway 与 Oauth2整合的关键,通过构建认证过滤器 AuthenticationWebFilter 完成Oauth2.0的token校验。

AuthenticationWebFilter 通过我们自定义的 ReactiveJdbcAuthenticationManager 完成token校验。

6. 搭建授权认证中心

SpringCloudAliaba 基于OAth2.0 搭建认证授权中心

二、搭建产品服务 2.1. 创建boot项目

模块名称product-serv

2.2. 引入依赖 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.gblfy</groupId> <artifactId>product-serv</artifactId> <version>1.0-SNAPSHOT</version> <!--https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E--> <properties> <java.version>1.8</java.version> <spring.cloud-version>Hoxton.SR9</spring.cloud-version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--服务注册发现--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies> <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> <!--spring-cloud-alibaba 版本控制--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.2.6.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> 2.3. controller package com.gblfy.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @RestController public class ProductController { //http://localhost:9000/product/" + productId @GetMapping("/product/{productId}") public String getProductName(@PathVariable Integer productId) { return "IPhone 12"; } } 2.4. 启动类 package com.gblfy; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProductAplication { public static void main(String[] args) { SpringApplication.run(ProductAplication.class); } } 2.5. 配置 server: port: 9000 spring: cloud: nacos: discovery: service: product-serv server-addr: localhost:8848 四、测试验证 4.1. 启动nacos

4.2. 启动认证中心

4.3. 启动产品服务

4.3. 请求认证授权中心

不携带token

4.4. 网关请求产品模块

通过网关请求产品服务,提示需要认证

4.5. 获取token

http://localhost:8081/oauth/token 通过认证授权中心获取toekn

grant_type:password client_id:app client_secret:app username:ziya password:111111

发起请求,获取token

4.6. 携带token请求产品服务

http://product.gblfy.com:8081/product/1

Authorization:Bearer d364c6cc-3c60-402f-b3d0-af69f6d6b73e

4.7. 直接请求产品服务

4.8. 请求结果比对

从4.6和4.7可以看出,当从授权中心获取token,携带token通过网关服务请求产品服务和直接请求产品服务效果是一样的。

五、总结

从以上测试结果可以看出gateway已经启动了一个统一认证授权的作用,对获取的token进行校验。以前我们所有的模块都需要集成认证授权模块,现在呢?所有的流量都从微服务网关SpringCloud Gateway走,那认证授权也是通过gateway来做的。因此,只需要在网关集成认证授权模块,其他的都不需要集成和配置。


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #文章目录一网关搭建1 #引入依赖2 #配置文件3 #增加权限管理器4 #自定义认证接口管理类5 #增加网关层的安全配置6