赞
踩
更新到这里呢,终于满足了读者两年的好奇心。从上年年底探索Spring Cloud GateWay
,众所周知,网关最大的用途在于限流、和路由,还有一个功能就是做鉴权,一直在我心中的疑惑,这个Spring Security OAuth2
是和网关怎么关联的呢,最近终于找出了答案。上图:
首先,介绍下流程
分享整体代码结构
已经上传到github上面, https://github.com/fafeidou/fast-cloud-nacos.git,欢迎fork。
配置application.yml,
没有配置redis,使用的默认配置
。
spring: application: name: security-api-gateway cloud: gateway: discovery: locator: lower-case-service-id: true #gateway可以通过开启以下配置来打开根据服务的serviceId来匹配路由,默认是false大写 enabled: true # 是否可以通过其他服务的serviceId来转发到具体的服务实例。默认为false routes: - id: security-auth uri: lb://security-auth # lb://serviceId predicates: - Path=/service-hi/auth/** filters: - StripPrefix=1 - id: security-provider uri: lb://security-provider # lb://serviceId predicates: - Path=/security-provider/** filters: - StripPrefix=1 - id: security-consumer uri: lb://security-consumer # lb://serviceId predicates: - Path=/security-consumer/** filters: - StripPrefix=1 nacos: discovery: server-addr: 127.0.0.1:8848 # 使用nacos作为注册中心
创建全局处理器·
TokenGlobalFilter
实现GlobalFilter
接口
public class TokenGlobalFilter implements GlobalFilter { @Autowired AuthService authService; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); String tokenFromCookie = authService.getTokenFromCookie(request); if (StringUtils.isEmpty(tokenFromCookie)) { ExceptionCast.cast(CommonCode.UNAUTHENTICATED); } //从header中取jwt String jwtFromHeader = authService.getJwtFromHeader(request); if (StringUtils.isEmpty(jwtFromHeader)) { //拒绝访问 ExceptionCast.cast(CommonCode.UNAUTHENTICATED); } //从redis取出jwt的过期时间 long expire = authService.getExpire(tokenFromCookie); if (expire < 0) { //拒绝访问 ExceptionCast.cast(CommonCode.FORBIDDEN); } return chain.filter(exchange); } }
添加gataway全局异常处理器
spring cloud gateway 是基于webflux的,用之前的controllerAdvice
,已经不生效了,这里需要我们自己去实现,这里有两个类JsonExceptionHandler
和ErrorHandlerConfiguration
,服务之间的调用controllerAdvice
是生效的。这里只给出代码片段。具体代码请看github。
public class JsonExceptionHandler extends DefaultErrorWebExceptionHandler { public JsonExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties, ErrorProperties errorProperties, ApplicationContext applicationContext) { super(errorAttributes, resourceProperties, errorProperties, applicationContext); } /** * 获取异常属性 */ @Override protected Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) { int code = 500; Throwable error = super.getError(request); if (error instanceof org.springframework.cloud.gateway.support.NotFoundException) { code = 404; } if (error instanceof fast.cloud.nacos.common.model.exception.CustomException) { CustomException customException = (CustomException) error; return response(code, customException.getResultCode().message()); } return response(code, this.buildMessage(request, error)); } /** * 指定响应处理方法为JSON处理的方法 * * @param errorAttributes */ @Override protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) { return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse); } /** * 根据code获取对应的HttpStatus * * @param errorAttributes */ @Override protected HttpStatus getHttpStatus(Map<String, Object> errorAttributes) { int statusCode = (int) errorAttributes.get("code"); return HttpStatus.valueOf(statusCode); } /** * 构建异常信息 * * @param request * @param ex * @return */ private String buildMessage(ServerRequest request, Throwable ex) { StringBuilder message = new StringBuilder("Failed to handle request ["); message.append(request.methodName()); message.append(" "); message.append(request.uri()); message.append("]"); if (ex != null) { message.append(": "); message.append(ex.getMessage()); } return message.toString(); } /** * 构建返回的JSON数据格式 * * @param status 状态码 * @param errorMessage 异常信息 * @return */ public static Map<String, Object> response(int status, String errorMessage) { Map<String, Object> map = new HashMap<>(); map.put("code", status); map.put("message", errorMessage); map.put("data", null); return map; } }
配置host
127.0.0.1 batman.com
启动
nacos
,api-gateway
,auth
,provider
服务,使用postman测试
查看nacos已经注册这几个服务:
获取令牌。
POST 请求: http://batman.com:40400/auth/userlogin
username和password都是batman
这个时候身份令牌已经写入cookie,jwt令牌写入redis。
获取jwt令牌
GET http://batman.com:40400/auth/userjwt
调用
provider
接口
GET 请求 http://batman.com:18085/security-provider/security/hello
错误实例
token传到有误
由于篇幅问题,这篇就先写到这里,下次会分享下,微服务之间调用如何传递token的问题以及怎样实现授权。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。