SpringCloud Gateway 集成 oauth2 实现统一认证授权_03
文章目录一、网关搭建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.
文章目录
一、网关搭建
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来做的。因此,只需要在网关集成认证授权模块,其他的都不需要集成和配置。
更多推荐
所有评论(0)