博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
用SpringCloud Alibaba搭建属于自己的微服务(二十八)~业务开发~gateway实现鉴权
阅读量:4204 次
发布时间:2019-05-26

本文共 11541 字,大约阅读时间需要 38 分钟。

一.概述

gateway作为客户端访问微服务的统一入口,鉴权是一件绕不开的事情,在前后端完全分离的今天,通常都是采用jwt生成token,客户端每次请求的时候都会把token放在请求头,gateway做校验,token解密没有问题,gateway就会路由分发到对应的微服务.之前的两个章节已经完成了登录和注册,这个章节写

二.server-user服务中加入解密token的接口.

package com.ccm.server.user.controller;import com.auth0.jwt.JWTVerifyException;import com.ccm.common.exception.result.ResultSet;import com.ccm.server.user.constants.ServerUserProperties;import com.ccm.server.user.controller.req.UserLoginReq;import com.ccm.server.user.controller.req.UserRegisterReq;import com.ccm.server.user.service.UserService;import com.ccm.server.user.util.JwtUtil;import io.swagger.annotations.Api;import io.swagger.annotations.ApiOperation;import io.swagger.annotations.ApiParam;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;import javax.validation.Valid;import java.io.IOException;import java.security.InvalidKeyException;import java.security.NoSuchAlgorithmException;import java.security.SignatureException;@Api(tags = "用户控制层")@RestController@RequestMapping(value = "user")public class UserController {
@Autowired private UserService userService; @Autowired private ServerUserProperties serverUserProperties; @ApiOperation(value = "注册") @PostMapping(value = "register") public ResultSet register(@Valid @RequestBody UserRegisterReq userRegisterReq) {
userService.register(userRegisterReq.getUsername(),userRegisterReq.getPassword()); return ResultSet.success(); } @ApiOperation(value = "登录") @PostMapping(value = "login") public ResultSet login(@Valid @RequestBody UserLoginReq userLoginReq) throws IOException {
String token = userService.login(userLoginReq.getUsername(),userLoginReq.getPassword()); return ResultSet.success(token); } @ApiOperation(value = "解析token") @GetMapping(value = "analysisToken") public ResultSet
analysisToken(@ApiParam(required = true,value = "token") @RequestParam(name = "token") String token) throws SignatureException, NoSuchAlgorithmException, JWTVerifyException, InvalidKeyException, IOException {
Long userId = JwtUtil.unsign(token, Long.class, serverUserProperties.getJwtSecretKey()); return ResultSet.success(userId); }}

三.gateway网关加入权限过滤器,这里我们使用实现AbstractGatewayFilterFactory接口的方式,方便我们在过滤器中自定义可配置的配置文件.

1.AuthFilter.

package com.ccm.gateway.filter;import com.alibaba.fastjson.JSONObject;import com.alibaba.fastjson.serializer.SerializerFeature;import com.alibaba.nacos.client.naming.utils.CollectionUtils;import com.ccm.common.exception.result.CodeEnum;import com.ccm.common.exception.result.ResultSet;import com.ccm.gateway.feign.ServerUserFeign;import lombok.Data;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.gateway.filter.GatewayFilter;import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;import org.springframework.core.io.buffer.DataBuffer;import org.springframework.http.MediaType;import org.springframework.http.server.reactive.ServerHttpRequest;import org.springframework.http.server.reactive.ServerHttpResponse;import org.springframework.stereotype.Component;import org.springframework.util.AntPathMatcher;import org.springframework.util.StringUtils;import org.springframework.web.server.ServerWebExchange;import reactor.core.publisher.Mono;import java.util.Arrays;import java.util.List;import java.util.Optional;/** *  @Description 鉴权过滤器 *  @Author ccm *  @CreateTime 2020/08/07 9:29 */@Slf4j@Componentpublic class AuthFilter extends AbstractGatewayFilterFactory
{
public AuthFilter() {
super(AuthFilterProperties.class); } /** * 默认需要放行的地址 */ private static final List
staticUriList = Arrays.asList("*.html","*.css","*.js","/v2/api-docs","*/v2/api-docs","/webjars/**"); /** * @Description 鉴权过滤器参数实体类映射 * @Author ccm * @CreateTime 2020/8/7 9:34 */ @Data public static class AuthFilterProperties {
private List
excludePatterns; //放行的接口路径 } @Autowired private AntPathMatcher antPathMatcher; @Autowired private ServerUserFeign serverUserFeign; @Override public GatewayFilter apply(AuthFilterProperties authFilterProperties) {
return (exchange, chain) -> {
//获取请求路径 ServerHttpRequest request = exchange.getRequest(); String uri = request.getURI().getPath(); log.info("鉴权过滤器接收到请求,uri={}",uri); //放行静态资源路径 for(String staticUri: staticUriList) {
if(antPathMatcher.match(staticUri,uri)) {
log.info("uri={},该路径为静态资源路径,放行",uri); return chain.filter(exchange); } } //放行配置了不需要做鉴权的路径 if(!CollectionUtils.isEmpty(authFilterProperties.getExcludePatterns())) {
for(String excludePattern: authFilterProperties.getExcludePatterns()) {
if(antPathMatcher.match(excludePattern,uri)) {
log.info("uri={},该路径为配置了无需鉴权,放行",uri); return chain.filter(exchange); } } } //从请求头获取token String token = Optional.ofNullable(request.getHeaders().get("ccm-token")) .map(t -> t.get(0)) .orElse(null); if(StringUtils.isEmpty(token)) {
//token为空,鉴权不通过,直接响应给客户端 return this.response(exchange,ResultSet.error(CodeEnum.NO_TOKEN,"未传入token","请先登录")); } //调用server-user服务的解析token的接口进行鉴权 ResultSet
resultSetFeign = serverUserFeign.analysisToken(token); if(!CodeEnum.SUCCESS.getCode().equals(resultSetFeign.getCode())) {
//鉴权失败,直接响应给客户端 return this.response(exchange,ResultSet.error(CodeEnum.ERROR_TOKEN,"token解析失败","登录信息错误")); } //走到这里,代表token有效,将token注入到请求头中,路由转发到微服务后,微服务接口就可以拿到该token对应的用户id ServerHttpRequest.Builder builder = request.mutate(); builder.headers(headers -> {
headers.set("ccm-userId",resultSetFeign.getData().toString()); }); return chain.filter(exchange); }; } /** * @Description 响应给客户端结果集 * @Author ccm * @CreateTime 2020/8/30 14:53 * @Params [exchange, resultSet] * @Return reactor.core.publisher.Mono
*/ public Mono
response(ServerWebExchange exchange, ResultSet resultSet) { ServerHttpResponse response = exchange.getResponse(); response.getHeaders().add("Content-Type", MediaType.APPLICATION_JSON_UTF8_VALUE); String responseBodyString = JSONObject.toJSONString(resultSet, SerializerFeature.WriteMapNullValue); DataBuffer bodyDataBuffer = response.bufferFactory().wrap(responseBodyString.getBytes()); return response.writeWith(Mono.just(bodyDataBuffer)); }}

2.ServerUserFeign.

package com.ccm.gateway.feign;import com.ccm.common.exception.result.ResultSet;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;/** *  @Description 调用server-user服务的feign层 *  @Author ccm *  @CreateTime 2020/08/07 9:27 */@FeignClient(name = "server-user")public interface ServerUserFeign {
@GetMapping(value = "user/analysisToken") ResultSet
analysisToken(@RequestParam(name = "token") String token);}

3.bootstrap中为server-user的路由加入该权限过滤器,并配置过滤器的自定义参数,可以配置不需要鉴权的接口路径,比如登录注册等.

server:  port: 200  #服务端口spring:  application:    name: gateway #服务名称  cloud:    nacos:      discovery:        server-addr: 47.96.131.185:8849 #nacos服务的注册和发现地址    #gateway组件相关配置    gateway:      discovery:        locator:          enabled: true #开启基于服务的注册和发现的路由转发,默认轮询模式      routes: #路由配置        - id: server-user #路由名称,不配默认为UUID          uri: lb://server-user #满足断言的路由到此服务          predicates: #为一个数组,每个规则为并且的关系            - Path=/api-user/** #断言表达式,如果args不写key的,会自动生成一个id,如下会生成一个xxx0的key,值为/foo/*          filters: #请求路由转发前执行的filter,为数组            - StripPrefix=1  #缩写,和name=StripPrefix,args,参数=1是一个意思,该过滤器为路由转发过滤去            - name: AuthFilter              args:                excludePatterns:  #不做权限校验的路径                  - /user/register                  - /user/login        - id: server-basic #路由名称,不配默认为UUID          uri: lb://server-basic #满足断言的路由到此服务          predicates: #为一个数组,每个规则为并且的关系            - Path=/api-basic/** #断言表达式,如果args不写key的,会自动生成一个id,如下会生成一个xxx0的key,值为/foo/*          filters: #请求路由转发前执行的filter,为数组            - StripPrefix=1  #缩写,和name=StripPrefix,args,参数=1是一个意思,该过滤器为路由转发过滤去

4.修改swagger的配置,为所有的接口的请求头加上ccm-token参数,方便我们调试.

package com.ccm.server.user.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import springfox.documentation.builders.ApiInfoBuilder;import springfox.documentation.builders.ParameterBuilder;import springfox.documentation.builders.PathSelectors;import springfox.documentation.builders.RequestHandlerSelectors;import springfox.documentation.schema.ModelRef;import springfox.documentation.service.ApiInfo;import springfox.documentation.service.Parameter;import springfox.documentation.spi.DocumentationType;import springfox.documentation.spring.web.plugins.Docket;import java.util.ArrayList;import java.util.List;/** * @Description swagger配置 * @Author ccm * @CreateTime 2020/7/8 17:38 */@Configurationpublic class SwaggerConfig {
@Bean public Docket createRestApi() {
List
pars = new ArrayList
(); ParameterBuilder ticketPar = new ParameterBuilder(); ticketPar.name("ccm-token").description("必要参数(白名单接口无需传递)") .modelRef(new ModelRef("string")).parameterType("header") .required(false).build(); //header中的ticket参数非必填,传空也可以 pars.add(ticketPar.build()); return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.ccm.server.user.controller")) //swagger接口扫描包 .paths(PathSelectors.any()).build().globalOperationParameters(pars); } private ApiInfo apiInfo() {
return new ApiInfoBuilder().version("1.0.0") .title("欢迎") .description("光临") .termsOfServiceUrl("www.baidu.com") .build(); }}

四.测试.

1.测试代码编写.

server-user服务中加入这个测试接口,可以看到请求头 有userId这个字段,在gateway中解密token后会将对应的用户id放入请求头,如果接口能够正确返回userId,证明测试成功.

package com.ccm.server.user.controller;import com.ccm.common.exception.result.ResultSet;import com.ccm.server.user.controller.req.UserRegisterReq;import io.swagger.annotations.Api;import io.swagger.annotations.ApiOperation;import io.swagger.annotations.ApiParam;import org.springframework.web.bind.annotation.*;import javax.validation.Valid;@Api(tags = "gateway鉴权测试")@RestController@RequestMapping(value = "gatewayAuthTest")public class GatewayAuthTestController {
@ApiOperation(value = "测试gateway是否把userId注入到了请求头") @GetMapping(value = "test01") public ResultSet test01(@ApiParam(hidden = true) @RequestHeader(name = "ccm-userId") String userId) {
return ResultSet.success("传入的token用户id为:"+userId); }}

1.启动gateway和server-user服务.

在这里插入图片描述

2.打开gateway的swagger界面.

在这里插入图片描述

3.调用注册接口获取token.

在这里插入图片描述

在这里插入图片描述

4.调用测试gateway鉴权的接口,并传入token.

在这里插入图片描述可以看到正确的返回了token对应的用户id

在这里插入图片描述
至此,完事!

您的点赞、收藏、转发和关注是我持续创作的动力!

源码地址:

转载地址:http://hktli.baihongyu.com/

你可能感兴趣的文章
网易社区被黑,黑客留言,截图为证
查看>>
好消息:Sun计划强化Java与Linux兼容性及开放部分Java源代码
查看>>
著名的菲尔人格测试,看看你适合做什么类型的工作
查看>>
[推荐]session原理详解
查看>>
Eclips使用秘技
查看>>
优化JDBC性能的三大技巧
查看>>
如何保护你的JSP页面
查看>>
GTalk 推出简体中文版
查看>>
[火星]汉芯被证实造假
查看>>
Google Trends-Google有趣的新服务
查看>>
好消息:Google推出Java开发Ajax框架
查看>>
世界上最靓最美的一对男女!!!
查看>>
java正则表达式详解
查看>>
Java中的String数据类型归纳
查看>>
JAVA程序编写规范
查看>>
JSP 编码规范
查看>>
代码大全中经典的两段话
查看>>
修改struts config文件不需重新发布的方法
查看>>
Oracle常用数据字典
查看>>
常见骗术集锦
查看>>