微服务下使用OAuth2实现网关安全

微服务下Spring Security实现Oauth2协议 入门篇我们介绍了如何在微服务下基于OAuth2协议构建认证服务器和资源服务器。资源服务器会检查请求头里面是否带上了token,并去认证服务器校验这个token是否合法,是否过期,是否有权限做对应的操作。显然随着微服务数量的增长,在每一个微服务上都要做资源服务器的配置实不可取的,因此我们需要把这部分的操作交由网关去处理。在上一节中check_token这部分的工作,资源服务器利用RemoteTokenServices帮我们实现了,但是这一节中我们需要在网关上自己手动实现这部分的逻辑。

OAuth2 网关安全架构图

网关逻辑上是资源服务器,需要订单服务,库存服务不再作为资源服务器。
在这里插入图片描述

认证服务器增加网关的配置

在这里插入图片描述
上一节中,订单服务的资源服务器配置相关代码可以删除了
在这里插入图片描述

新增网关服务

添加依赖
	<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
		</dependency>

开启@EnableZuulProxy

@SpringBootApplication
@EnableZuulProxy
public class GatewayServer {
	public static void main(String[] args) {
		SpringApplication.run(GatewayServer.class, args);
	}

}

配置路由规则application.yml

zuul:
  routes:
    token:
      url: http://localhost:9090
    order:
      url: http://localhost:9080
    price:
      url: http://localhost:9060
  sensitive-headers:
/**
 * 定义zuul pre前置过滤器
 *
 */
@Slf4j
@Component
public class OAuthFilter extends ZuulFilter {
	
	private RestTemplate restTemplate = new RestTemplate();

	@Override
	public boolean shouldFilter() {
		return true;
	}

	@Override
	public Object run() throws ZuulException {
		
		log.info("oauth start");
		// 为了获取request请求
		RequestContext requestContext = RequestContext.getCurrentContext();
		HttpServletRequest request = requestContext.getRequest();

		// 获取token请求不进行过滤
		if(StringUtils.startsWith(request.getRequestURI(), "/token")) {
			return null;
		}
		
		String authHeader = request.getHeader("Authorization");
		
		if(StringUtils.isBlank(authHeader)) {
			return null;
		}

		// 如果请求头带了以barer开头的请求,则去认证服务器校验token
		if(!StringUtils.startsWithIgnoreCase(authHeader, "bearer ")) {
			return null;
		}
		
		try {

			// 将token放入请求头里
			TokenInfo info = getTokenInfo(authHeader);
			request.setAttribute("tokenInfo", info);
			
		} catch (Exception e) {
			log.error("get token info fail", e);
		}
		
		return null;
	}

	private TokenInfo getTokenInfo(String authHeader) {
		
		String token = StringUtils.substringAfter(authHeader, "bearer ");
		String oauthServiceUrl = "http://localhost:9090/oauth/check_token";
		
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
		headers.setBasicAuth("gateway", "123456");
		
		MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
		params.add("token", token);
		
		HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(params, headers);
		
		ResponseEntity<TokenInfo> response = restTemplate.exchange(oauthServiceUrl, HttpMethod.POST, entity, TokenInfo.class);
		
		log.info("token info :" + response.getBody().toString());
		
		return response.getBody();
	}

	/**
	 * zuul 提供了四种过滤器类型
	 * pre
	 * post
	 * error
	 * route
	 * @return
	 */
	@Override
	public String filterType() {
		return "pre";
	}

	@Override
	public int filterOrder() {
		return 1;
	}

}

AuthorizationFilter

/**
 *
 * 授权过滤器
 */
@Slf4j
@Component
public class AuthorizationFilter extends ZuulFilter {

	@Override
	public boolean shouldFilter() {
		return true;
	}

	@Override
	public Object run() throws ZuulException {
		
		log.info("authorization start");
		
		RequestContext requestContext = RequestContext.getCurrentContext();
		HttpServletRequest request = requestContext.getRequest();
		
		if(isNeedAuth(request)) {

			// OAuthFilter token 校验通过,会将token信息放在请求头里
			TokenInfo tokenInfo = (TokenInfo)request.getAttribute("tokenInfo");

			// 校验是否有权限
			if(tokenInfo != null && tokenInfo.isActive()) {
				if(!hasPermission(tokenInfo, request)) {
					log.info("audit log update fail 403");
					handleError(403, requestContext);
				}
				
				requestContext.addZuulRequestHeader("username", tokenInfo.getUser_name());
			}else {
				if(!StringUtils.startsWith(request.getRequestURI(), "/token")) {
					log.info("audit log update fail 401");
					handleError(401, requestContext);
				}
			}
		}
		
		return null;
	}
	
	private void handleError(int status, RequestContext requestContext) {
		requestContext.getResponse().setContentType("application/json");
		requestContext.setResponseStatusCode(status);
		requestContext.setResponseBody("{\"message\":\"auth fail\"}");
		requestContext.setSendZuulResponse(false);
	}

	private boolean hasPermission(TokenInfo tokenInfo, HttpServletRequest request) {
		return true; //RandomUtils.nextInt() % 2 == 0;
	}

	private boolean isNeedAuth(HttpServletRequest request) {
		return true;
	}

	@Override
	public String filterType() {
		return "pre";
	}

	@Override
	public int filterOrder() {
		return 3;
	}

}

代码结构
在这里插入图片描述

postman测试

网关basic认证
请求token
请求订单服务,带上token

网关限流

网关上的限流工作只做适合粗粒度的限流,不要做业务上的线流,网关与网关后面的微服务之间需要是低耦合的。因为业务规则改变了,需要调整网关上的限流规则,甚至要重新部署,这是不可取的。
这一小节内容主要介绍网关上如何做限流操作,这里我们使用了开源组件spring-cloud-zuul-ratelimit

引入依赖:

	<dependency>
			<groupId>com.marcosbarbero.cloud</groupId>
			<artifactId>spring-cloud-zuul-ratelimit</artifactId>
			<version>2.2.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>

限流规则配置:

  # 限流的配置 这里存在数据库中,生产环境时候 需要放在redis中
#  ratelimit:
#    enabled: true
#    repository: JPA
#    default-policy-list:
#    - limit: 2
#      quota: 1
#      refresh-interval: 3
#      type:
#        - url  /a get # 针对指定的url进行限制
#        - httpmethod
#  jpa:
#    generate-ddl: true
#    show-sql: true

网关用到了数据库,因此还有数据源的配置,这里不贴出来了

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 微服务鉴权中心是指在微服务架构中,使用一个独立的鉴权中心来统一管理和验证各个微服务的访问权限。而网关是作为微服务鉴权中心的入口,负责对外提供服务,同时也是微服务的访问控制和认证的第一层防线。在网关配置中使用Spring Security OAuth2是一种常见的实现方式。 首先,需要在网关项目中引入Spring Security OAuth2的依赖。可以使用Maven或Gradle将所需的依赖加入到项目的配置文件中。 接下来,需要配置Spring Security OAuth2的相关信息,包括鉴权中心的认证服务器地址、客户端ID和密钥等。这些配置可以在网关项目的配置文件中设置。 然后,需要配置网关的请求过滤器,用于拦截并验证请求。可以实现一个自定义的过滤器,继承自Spring Security的AbstractAuthenticationProcessingFilter类,并覆写其中的相关方法。 在过滤器中,需要使用OAuth2的认证流程来验证请求的合法性。可以使用RestTemplate发送请求到鉴权中心的认证服务器,获取访问令牌和授权信息。 接着,可以通过OAuth2AuthenticationManager来管理认证信息,并创建并设置Spring Security的Authentication对象。可以使用自定义的处理器将认证信息传递给后续的微服务。 最后,需要对网关的接口进行权限的配置。可以使用Spring Security的注解来定义接口的访问权限,如@PreAuthorize和@Secured。 通过以上的配置,网关就可以实现对请求的鉴权和认证了。对于未经授权的请求,网关会拒绝访问,并返回相应的错误信息。对于经过鉴权验证的请求,网关会将请求转发给相应的微服务进行处理。同时,网关还可以对返回的结果进行处理和加工,以满足特定的需求。 总之,配置Spring Security OAuth2可以有效地实现微服务鉴权中心之网关的权限管理和访问控制,提高系统的安全性和可维护性。 ### 回答2: 微服务鉴权中心作为一个独立的服务单元,负责管理和维护整个微服务体系的权限和鉴权机制。网关微服务系统的入口,负责接收和处理所有外部请求。 在配置Spring Security OAuth2时,我们可以将鉴权中心和网关进行整合,以实现统一的认证和授权机制。在网关配置文件中,我们首先需要引入Spring Security OAuth2的依赖,然后定义一些必要的配置项。 首先,我们需要配置认证服务器的地址,通常是鉴权中心的地址。也就是说,所有的认证和授权请求都会转发到该地址进行处理。配置项如下: ``` security: oauth2: client: accessTokenUri: <鉴权中心地址>/oauth/token userAuthorizationUri: <鉴权中心地址>/oauth/authorize clientId: <客户端ID> clientSecret: <客户端密钥> ``` 其中,`accessTokenUri`表示获取访问令牌的地址,`userAuthorizationUri`表示用户授权的地址,`clientId`和`clientSecret`是鉴权中心针对网关颁发的客户端ID和密钥。 接下来,我们需要配置资源服务器的地址,即微服务的地址。配置项如下: ``` security: oauth2: resource: userInfoUri: <微服务地址>/user ``` 其中,`userInfoUri`表示获取用户信息的地址。 最后,我们需要配置路由规则,指定哪些请求需要进行认证和授权。配置项如下: ``` spring: cloud: gateway: routes: - id: <路由ID> uri: <目标URI> predicates: - Path=/api/** filters: - TokenRelay metadata: authorization-uri: <鉴权中心地址>/oauth/authorize ``` 其中,`id`表示路由的唯一标识,`uri`表示目标URI,`predicates`指定路由的条件,`filters`指定过滤器,`metadata`指定认证和授权的地址。 通过以上配置,我们可以实现网关和鉴权中心的整合,实现微服务系统的统一认证和授权机制。网关会将认证和授权请求转发到鉴权中心进行处理,并根据鉴权中心返回的结果进行相应的操作。 ### 回答3: 在微服务架构中,鉴权是一个非常重要的部分,它能够确保只有经过授权的用户才能访问特定的服务。而网关作为整个系统的入口,负责转发和路由请求,需要配置Spring Security和OAuth2来实现鉴权。 首先,需要在网关服务中添加Spring Security和OAuth2的相关依赖,并在配置文件中开启相关功能。接着,需要创建一个自定义的鉴权过滤器,该过滤器会在请求到达网关之后进行鉴权操作。 在过滤器中,首先需要配置一个OAuth2的资源服务。这个资源服务可以通过配置一个TokenStore来获取和保存令牌信息。然后,可以配置一个JwtAccessTokenConverter来验证和解析令牌。同时,需要配置一个ResourceServerTokenServices,该服务会验证令牌的正确性,是否过期等信息。 接下来,在过滤器中需要配置spring security的认证管理器,该管理器会根据请求头中的令牌信息来进行用户的鉴权操作。可以使用一个自定义的UserDetailsService来获取用户的权限信息,并将权限信息添加到SecurityContext中,以便后续的鉴权操作。 最后,在过滤器中,可以配置一些具体的鉴权规则,比如某些URL需要特定的角色才能访问,可以用.antMatchers().hasRole()的方式进行配置。 通过以上步骤,就可以实现网关的鉴权功能。当用户发起请求时,网关会根据请求头中的令牌信息进行鉴权,只有经过授权的用户才能访问特定的服务。这样可以确保整体系统的安全性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值