网站开发职能,东莞网站建设dgjcwl,wordpress 页面压缩,wordpress自带文章类型gateway#xff1a;网关#xff0c;我们都知道网关的作用就是对系统的所有请求#xff0c;网关都会进行拦截#xff0c;然后做一些操作#xff08;例如#xff1a;设置每个请求的请求头httpHeader#xff0c;身份认证等等#xff09;此时一般会使用到网关过滤器#x…gateway网关我们都知道网关的作用就是对系统的所有请求网关都会进行拦截然后做一些操作例如设置每个请求的请求头httpHeader身份认证等等此时一般会使用到网关过滤器创建一个过滤器去实现GlobalFilter接口
jwt:JSON-WEB-TOKEN这里就不过多解释了同学们可以自行搜索相关文章它主要包含三个部分更好的生成token的一种行业规范主要作用就是令牌校验。
{ header:xxx, payload:xxx, singature:xxx
}
redis的主要作用就是为了存放用户信息该用户信息主要包含以下几个字段
{userCode:xxx,language:xxx,主要是为了进行国际化语言配置比如ZH-CN 、ENmenuApu:[xxx,xxx,...xxx] 用户对应的菜单权限列表APIjwtToken:xxx 必须拥有这个字段为了防止同一个用户在不同机器上登录进行操作
}
httpHeader的主要作用就是存放userCode用于任何请求都可以获取到当前操作的用户名比如当我要添加一个新增接口或者更新接口一般会有两个字段一个是creator一个是modifier那么这两个值就可以直接取请求头的userCode。
还有就是存放language为了进行国际化语言切换。
所以现在我先给大家画一个图待会写的代码也是按照这个逻辑方便大家加深理解与记忆。 现在看一下代码逻辑
我们创建一个gateway全局过滤器AuthTokenGlobalFilter 实现GlobalFilter接口重写如下方法
public MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain)
public MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request exchange.getRequest();HttpHeaders headers request.getHeaders();//得到前端访问请求时的请求头HttpHeaders httpHeaders new HttpHeaders();httpHeaders.putAll(headers);//先过滤掉登录请求登录请求不需要进行拦截直接放行if (ObjectUtil.equals(request.getURI().getPath(), /login)) {//放行之前将前端发出请求时的请求头拿到同时设置一下Accept-LanguagehttpHeaders.setAcceptLanguage(httpHeaders.getAcceptLanguage());//放行chain.filter(exchange.mutate().request(decorateRequest(request, httpHeaders)).build());}//其他请求//判读1请求的请求头中是否携带tokenif (CollectionUtils.isEmpty(headers.get(Authorization))) {throw new ServiceException(Codes.NO_TOKEN_RECOGNIZED);//未识别到token}//判断2token解析出来的数据是否能在redis中查询到考虑到token有可能发生过期或者是登出了redis中的数据就会删除String authorization headers.get(Authorization).get(0);//通过token 利用jwt反解析出数据 claimsJSONObject jsonObject JwtUtils.parseJwtToken(authorization, user, NetworkUtils.getIp(request));if (ObjectUtil.isNull(jsonObject)) {throw new ServiceException(Codes.AUTHORIZATION_ERROR);}//如果是登出请求/logout需要在请求头中加上userCode为了后续放行之后拿到该userCode作为redis的key的一部分去删除登录时存到redis中的数据if (ObjectUtil.equals(request.getURI().getPath(), /logout)) {httpHeaders.set(userCode, jsonObject.get(userCode).toString());return chain.filter(exchange.mutate().request(decorateRequest(request, httpHeaders)).build());}//拿到userCodeString userCode (String) jsonObject.get(userCode);//去redis中获取数据 key》 User:userCode 比如User:zkwString key USER_CACAHE_PREFIX userCode;String jsonString stringRedisTemplate.opsForValue().get(key);LoginUser loginUser JSONObject.parseObject(jsonString, LoginUser.class);if (ObjectUtils.isEmpty(loginUser)) {throw new ServiceException(Codes.LOGIN_EXPIRED);}//走到这里//1、发出的请求的请求头中携带了token//2、携带的token有效没有过期或者是登出//那么此时还需要进行判断3判断携带的token与redis中存储的token是否一致因为有可能有这么一个情况// A用户在机器172.16.254.1上登录一次成功之后就会在redis中存放A对应的token以及其他一些字段数据//那么此时在A发出另外一个请求之前比如查询此时A用户又在另外一台机器上172.16.254.2登录成功之后也会在redis中存放A在172.16.254.2对应的token以及其他一些字段数据此时就会覆盖前面redis中存放A在172.16.254.1的token数据了// 因为生成的token是有根据ip地址的所以当A用户在机器上172.16.254.1发起请求的时候携带的token就与此时redis中的token是不对的了所以对于这种情况我们就不允许存在。if (!ObjectUtil.equals(authorization, loginUser.getJwtToken())) {throw new ServiceException(Codes.LOGIN_EXPIRED);}//如果token都满足情况了就代表确确实实身份无误了那么就需要进行用户的权限列表判断了。}
上面是认证逻辑现在是授权逻辑了。
redis中有两个key一个key就是刚刚关于认证token的另一个就是授权的。AccessControl:permissions这个key的主要作用就是看看前端发出的请求是否是系统里面已经配置的菜单权限API防止随意伪造请求API。 接下来我们看看授权的代码
//***********************************************授权*************************************************************//异步获取统里面配置的存在的菜单api在redis中有存放两个key一个key就是刚刚前面关于token的另外一个是关于菜单权限api的//AccessControl:permissionsMenu 这个值的主要作用就是为了判断你请求的api是否此时系统里面有如果没有的话有两个原因// 1、你xjb自己随便别写一个请求 2、确确实实开发了这个接口但是可能在系统菜单表里面忘记添加了//User:userCodeCompletableFutureString future CompletableFuture.supplyAsync(() - {return stringRedisTemplate.opsForValue().get(AccessControl:permissionsMenu);});String permissionsMenu future.join();//如果不为空就代表此时系统里面已经配置了菜单权限apiif (permissionsMenu ! null) {String[] urls permissionsMenu.split(,);//当前url不存在系统配置的菜单权限api里面 直接放行if (Arrays.stream(urls).noneMatch(part - (part.trim()).equals(requestPath))) {return chain.filter(exchange.mutate().request(decorateRequest(request, httpHeaders)).build());}}//如果菜单为空证明是第一次请求到把系统里面配置的存在的api设置到该key上else {//通过openFeign远程调用获取系统配置的菜单权限APIListString urls permissionFeign.queryPermissionsMenu();stringRedisTemplate.opsForValue().set(AccessControl:permissionsMenu, urls.toString());//当前url不存在系统配置的菜单权限api里面 直接放行if (urls.stream().noneMatch(part - (part.trim()).equals(requestPath))) {return chain.filter(exchange.mutate().request(decorateRequest(request, httpHeaders)).build());}}//如果存在如果判断当前用户是否拥有这个菜单权限apiboolean flag sysMenuService.queryUserMenuButton(userCode).stream().anyMatch(arg - ObjectUtil.equals(arg.getApi(), requestPath));if (!flag) {throw new ServiceException(Codes.USER_INSUFFICIENT_PERMISSIONS);}return chain.filter(exchange.mutate().request(decorateRequest(request, httpHeaders)).build());
完整代码 注意
登录接口是不需要进行拦截的我们在登录接口的时候如果登录成功才会生成一个token生成token以及代码中的反解析token在我上一篇文章的工具类有同学们也可以去看一下还有查询该用户对应的菜单权限API列表然后会把相关信息存到redis中去。
总结 最后
如果大家觉得这篇文章对你们有所帮助的话麻烦给个免费的赞赞也祝各位码农在未来的IT道路上越走越远谢谢