文章目录 一、网关搭建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. 获取tokenhttp://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