Spring Cloud微服务架构从入门到会用(五)—服务网关鉴权

Stella981
• 阅读 817

上一篇文章我们集成了服务网关Spring Cloud Gateway,所有的服务请求都可以通过Gateway访问。那我们就可在服务网关这一层对用户的请求进行鉴权,判断是否可以访问路由的API接口。

接下来我们开始增加鉴权,这里我们使用jwt

1. 创建授权服务module

按照第二篇文章创建一个module,起名为app-auth。

2. 修改service-auth的pom文件

<properties>        <java.version>1.8</java.version>        <spring-cloud.version>Hoxton.SR1</spring-cloud.version>    </properties>    <dependencies>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-openfeign</artifactId>        </dependency>        <!-- redis -->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-data-redis</artifactId>        </dependency>        <dependency>            <groupId>org.apache.commons</groupId>            <artifactId>commons-pool2</artifactId>            <version> 2.6.1</version>        </dependency>        <!--  jwt鉴权  -->        <dependency>            <groupId>com.auth0</groupId>            <artifactId>java-jwt</artifactId>            <version>3.10.0</version>        </dependency>        <dependency>            <groupId>cn.hutool</groupId>            <artifactId>hutool-all</artifactId>            <version>5.2.0</version>        </dependency>        <dependency>            <groupId>org.projectlombok</groupId>            <artifactId>lombok</artifactId>            <optional>true</optional>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>            <scope>test</scope>            <exclusions>                <exclusion>                    <groupId>org.junit.vintage</groupId>                    <artifactId>junit-vintage-engine</artifactId>                </exclusion>            </exclusions>        </dependency>    </dependencies>    <dependencyManagement>        <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>

3. 修改启动类

@SpringBootApplication@EnableEurekaClient  // 开启eureka客户端模式public class AppAuthApplication {    public static void main(String[] args) {        SpringApplication.run(AppAuthApplication.class, args);    }}

4. 增加jwt工具类

这里我们将生成的token和refreshToken都存储到redis中,所以我们也引入了redis依赖。为了保证一定的安全性,我们的token和refreshToken都是有时效的,这里我们将这两个值的有效时间放到配置文件中,以便灵活修改。

在生成token的时候,我们将账号和客户端类型传入,客户端类型主要分为,网页浏览器端,手机app端和桌面程序端。三端都有各自的token。

// 生成token和refreshTokenpublic Map<String, String> getToken(String phone, String type){        //生成refreshToken        String refreshToken = UUID.randomUUID().toString().replaceAll("-","");        String token = this.buildJWT(phone, type);        String key = SecureUtil.md5(type + phone);        //向hash中放入数值        stringRedisTemplate.opsForHash().put(key,"token", token);        stringRedisTemplate.opsForHash().put(key,"refreshToken", refreshToken);        //设置key过期时间        stringRedisTemplate.expire(key, refreshTokenExpireTime, TimeUnit.MILLISECONDS);        Map<String , String> map = new HashMap<>(2);        map.put("token", token);        map.put("refreshToken", refreshToken);        return map;    }

5. 编写登录接口

@GetMapping(value = "/login")    public ResponseDTO<Map<String, String>> login(@RequestParam("account") String account,                                                  @RequestParam("password") String password,                                                  HttpServletRequest request){        String clientType = request.getHeader("clientType");        if(StrUtil.isEmpty(clientType)) {            return ResponseDTO.error().setMsg("clientType不能为空");        }        //账号密码校验        if(StrUtil.isNotEmpty(account) && StrUtil.isNotEmpty(password)){            if ("admin".equals(account) && "123456a".equals(password)){                Map<String, String> map = tokenUtil.getToken(account, clientType);                return ResponseDTO.ok().setData(map);            }else {                return ResponseDTO.error().setMsg("账号或密码错误");            }        }else {            return ResponseDTO.error().setMsg("账号或密码错误");        }    }

此处暂时只展示登录方法,刷新token方法和退出登录方法请查看git仓库。

5. server-gateway增加Filter,用于权限验证

此处代码较多,只展示部分核心代码

创建RequestGlobalFilter类,增加@Component注解,实现GlobalFilter, Ordered这两个接口;实现public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) 方法

由于这个Filter拦截所有请求,所以我们要提前定义一下不拦截的接口,例如登录接口和刷新token的接口。我们在server-gateway的配置文件中增加ignore.urls,把不拦截的接口用逗号分隔的方式放到这个配置上,例如:

ignore.urls=/auth/login,/authrefresh

对于需要拦截的url,我们需要从header中拿到token和clientType,然后通过jwt工具进行校验该token是否有效,如果无效返回401错误信息。

private String verifyJWT(String token, String clientType) {        String userPhone;        try {            Algorithm algorithm = Algorithm.HMAC256(clientType);            JWTVerifier verifier = JWT.require(algorithm)                    .withIssuer("userPhone")                    .build();            DecodedJWT jwt = verifier.verify(token);            userPhone = jwt.getClaim("phone").asString();        } catch (JWTVerificationException e) {            return "";        }        return userPhone;    }

在此处我们需要引入jwt依赖

<dependency>            <groupId>com.auth0</groupId>            <artifactId>java-jwt</artifactId>            <version>3.10.0</version>        </dependency>

6. 增加app-auth路由配置

# 路由到app-auth服务spring.cloud.gateway.routes[2].id=app-authspring.cloud.gateway.routes[2].uri=lb://APP-AUTHspring.cloud.gateway.routes[2].predicates[0]=Path=/auth/**spring.cloud.gateway.routes[2].filters[0]=StripPrefix=1

7. 启动服务测试请求

分别启动server-eureka,server-gateway,app-auth,app-order,app-storage

由于本文涉及的代码偏多,请大家移步至git仓库查看更多代码。 https://gitee.com/hedavid/spring-cloud-example

本文分享自微信公众号 - 自增程序员(javaipp)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
Docker 部署SpringBoot项目不香吗?
  公众号改版后文章乱序推荐,希望你可以点击上方“Java进阶架构师”,点击右上角,将我们设为★“星标”!这样才不会错过每日进阶架构文章呀。  !(http://dingyue.ws.126.net/2020/0920/b00fbfc7j00qgy5xy002kd200qo00hsg00it00cj.jpg)  2
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Java服务总在半夜挂,背后的真相竟然是... | 京东云技术团队
最近有用户反馈测试环境Java服务总在凌晨00:00左右挂掉,用户反馈Java服务没有定时任务,也没有流量突增的情况,Jvm配置也合理,莫名其妙就挂了
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这