request.getPath()和获取ip地址、端口号

本文介绍了如何通过HTTP请求的不同部分来确定项目的访问路径,包括getContextPath、getServletPath、getRequestURI和getRequestURL等方法的使用,并提供了获取服务器和客户端IP地址及端口号的方法。

project为项目名,访问路径如下:

------------http://localhost:8088/project/uploadFile

 

------------/project                       request.getContextPath()

------------/uploadFile                  request.getServletPath()

------------/project/uploadFile      request.getRequestURI()

------------http://localhost:8888/me/uploadFile     request.getRequestURL()

获取服务端ip地址                       request.getLocalAddr()

获取客户端ip地址                       request.getRemoteAddr()

获取端口号                                  request.getServerPort()

package com.ssr.travel.scenery.frame.gateway.filter.oauth; import com.alibaba.fastjson2.JSONObject; import com.ssr.travel.scenery.biz.manage.model.co.api.ApiCacheConst; import com.ssr.travel.scenery.biz.manage.model.co.app.AppCacheConst; import com.ssr.travel.scenery.biz.manage.model.co.oauth.OauthCacheConst; import com.ssr.travel.scenery.biz.manage.model.co.staff.StaffsCacheConst; import com.ssr.travel.scenery.biz.manage.model.dto.oauth.OauthInfoParam; import com.ssr.travel.scenery.biz.manage.model.eo.api.ApiOauth; import com.ssr.travel.scenery.biz.manage.model.eo.api.ApiProtocol; import com.ssr.travel.scenery.biz.manage.model.eo.app.AppResultEnum; import com.ssr.travel.scenery.biz.manage.model.eo.authority.AuthorityResultEnum; import com.ssr.travel.scenery.biz.manage.model.eo.oauth.OauthResultEnum; import com.ssr.travel.scenery.biz.manage.model.eo.user.UserTypeEnum; import com.ssr.travel.scenery.biz.manage.model.vo.response.app.AppResponse; import com.ssr.travel.scenery.biz.manage.tool.util.api.ApiUtil; import com.ssr.travel.tool.boot.model.bo.result.Results; import com.ssr.travel.tool.boot.model.co.symbol.SymbolEnConst; import com.ssr.travel.tool.boot.util.collection.CollectionUtil; import com.ssr.travel.tool.db.model.io.cache.Cache; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.http.HttpStatus; 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.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.Objects; import java.util.Set; /** * @Author 说淑人 * @Datetime 2024/4/21 22:49 * @Description 授权过滤器类 */ @Slf4j @Component public class OauthFilter implements GlobalFilter, Ordered { private static final byte[] NO_CARRY_APP_CODE_BYTES; private static final byte[] NOT_CARRY_TOKEN_BYTES; private static final byte[] TOKEN_INVALID_BYTES; private static final byte[] TOKEN_EXPIRED_BYTES; private static final byte[] NO_ACCESS_AUTHORITY_BYTES; private static final byte[] AUTHORITY_EXPIRED_BYTES; static { Results<Void> results = Results.fail(AppResultEnum.NO_CARRY_APP_CODE); NO_CARRY_APP_CODE_BYTES = JSONObject.toJSONString(results).getBytes(StandardCharsets.UTF_8); results = Results.fail(OauthResultEnum.NOT_CARRY_TOKEN); NOT_CARRY_TOKEN_BYTES = JSONObject.toJSONString(results).getBytes(StandardCharsets.UTF_8); results = Results.fail(OauthResultEnum.TOKEN_INVALID); TOKEN_INVALID_BYTES = JSONObject.toJSONString(results).getBytes(StandardCharsets.UTF_8); results = Results.fail(OauthResultEnum.TOKEN_EXPIRED); TOKEN_EXPIRED_BYTES = JSONObject.toJSONString(results).getBytes(StandardCharsets.UTF_8); results = Results.fail(AuthorityResultEnum.NO_ACCESS_AUTHORITY); NO_ACCESS_AUTHORITY_BYTES = JSONObject.toJSONString(results).getBytes(StandardCharsets.UTF_8); results = Results.fail(AuthorityResultEnum.AUTHORITY_EXPIRED); AUTHORITY_EXPIRED_BYTES = JSONObject.toJSONString(results).getBytes(StandardCharsets.UTF_8); } @Autowired private RedisTemplate<String, Object> redisTemplate; /** * 过滤 * * @param exchange 交换 * @param chain 网关过滤器链 * @return 无 */ @Override @SuppressWarnings("unchecked") public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpResponse response = exchange.getResponse(); response.setStatusCode(HttpStatus.OK); response.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8); // ---- 一级鉴权:判断请求是否携带应用编码/相应应用是否存在/禁用,否/否/是则返回相 // 关异常。 Results<Void> results; ServerHttpRequest request = exchange.getRequest(); String appCode = request.getHeaders().getFirst("appCode"); AppResponse appResponse; if (StringUtils.isBlank(appCode)) { // ---- 如果未携带应用编码,返回相关异常。 DataBuffer buffer = response.bufferFactory().wrap(NO_CARRY_APP_CODE_BYTES); return response.writeWith(Mono.just(buffer)); } else if (Objects.isNull(appResponse = (AppResponse) redisTemplate.opsForHash().get(AppCacheConst.APP_MAP_DATA, appCode))) { // ---- 如果应用不存在,返回相关异常。 results = Results.fail(AppResultEnum.APP_NOT_EXIST, appCode); byte[] data = JSONObject.toJSONString(results).getBytes(StandardCharsets.UTF_8); DataBuffer buffer = response.bufferFactory().wrap(data); return response.writeWith(Mono.just(buffer)); } else if (appResponse.isDisable()) { // ---- 如果应用已禁用,返回相关异常。 results = Results.fail(AppResultEnum.APP_DISABLED, appCode); byte[] data = JSONObject.toJSONString(results).getBytes(StandardCharsets.UTF_8); DataBuffer buffer = response.bufferFactory().wrap(data); return response.writeWith(Mono.just(buffer)); } // ---- 获取接口协议。 String apiProtocol = request.getHeaders().getFirst("Upgrade"); apiProtocol = StringUtils.isBlank(apiProtocol) ? ApiProtocol.HTTP_CODE : ApiProtocol.WEBSOCKET_CODE; log.info("接口协议:{}", apiProtocol); // ---- 获取接口重写路径。如果我们配置了重写规则,那么接口原始路径会被网关自带的重 // 写过滤器所重写以转变为向各微服务请求的真实地址。因此当前过滤器的必须晚于网关自 // 带的重写过滤器执行。 URI uri = exchange.getRequest().getURI(); String apiRewritePath = uri.getPath(); log.info("接口重写路径:{}", apiRewritePath); // ---- 获取接口权限。所谓权限其实就是“域名/IP:端口”,其不随路径重写而变化。因为真 // 正的转发是在网关内部完成的,包含负载均衡等逻辑,并不直接对外暴露。如果有相应的 // 诉求,需要使用其它写法获取,这里我们暂时没有这种诉求。 String apiAuthority = uri.getAuthority(); log.info("接口权限(域名/IP:端口):{}", apiAuthority); // ---- 获取接口方式。接口方式需要大写处理,以避免大小写问题导致后续判断错误。 String apiMethod = request.getMethodValue().toUpperCase(); log.info("接口方式:{}", apiMethod); // ---- 拼接接口编码。基于重写的真实接口生成接口编码,获取接口的协议/路径/方式以拼 // 接为接口编码。 String apiCode = ApiUtil.generateCode(apiProtocol, apiRewritePath, apiMethod); log.info("接口编码:{}", apiCode); // ---- 二级鉴权:判断当前接口是否存在&是否免鉴权,即判断接口编码是否存在授权集& // 授权集中是否包含"avoid",是则返回相关异常/直接通过过滤。 String dataKey = ApiCacheConst.API_OAUTHS_MAP_DATA; // ---- 如果接口授权集不存在,直接返回404异常回应。 Set<String> apiOauths = (Set<String>) redisTemplate.opsForHash().get(dataKey, apiCode); if (CollectionUtil.isNotValid(apiOauths)) { response.setStatusCode(HttpStatus.NOT_FOUND); return response.setComplete(); } log.info("接口授权集:{}", apiOauths); boolean oauthAvoid = apiOauths.contains(ApiOauth.AVOID_CODE); if (oauthAvoid) { // ---- 如果当前接口免除授权,那么删除当前请求中关于令牌的内容,因为Spring Cloud // Oauth2有个BUG,即即使设置了免授权,但只要携带了令牌则还是会进行校验,因此 // 对于免授权的接口需要将其请求头中可能携带的的令牌信息删除。这是非常必要的, // 因为前端通常不会特意删除免接口中关于令牌的数据。因为对他们来说,既然接口已经 // 免授权了,那么带不带应该是一样的。而此时如果携带了令牌,那么请求就会在通过 // 网关层后被业务层被拦截。 ServerHttpRequest.Builder serverHttpRequestBuilder = exchange.getRequest().mutate(); serverHttpRequestBuilder.headers(httpHeaders -> { httpHeaders.remove("Authorization"); }); ServerHttpRequest serverHttpRequest = serverHttpRequestBuilder.build(); exchange.mutate().request(serverHttpRequest).build(); return chain.filter(exchange); } // ---- 三级鉴权:判断当前请求是否携带令牌/令牌是否合法/令牌是否过期,是则返回相应 // 异常回应。 String authorization = exchange.getRequest().getHeaders().getFirst("Authorization"); String[] authorizations; OauthInfoParam oauthInfo; if (StringUtils.isBlank(authorization)) { // ---- 令牌不存在。 DataBuffer buffer = response.bufferFactory().wrap(NOT_CARRY_TOKEN_BYTES); return response.writeWith(Mono.just(buffer)); } else if ((authorizations = authorization.split(SymbolEnConst.BLANK)).length != 2) { // ---- 令牌不合法。 DataBuffer buffer = response.bufferFactory().wrap(TOKEN_INVALID_BYTES); return response.writeWith(Mono.just(buffer)); } else if (Objects.isNull(oauthInfo = (OauthInfoParam) redisTemplate.opsForValue().get( Cache.key(OauthCacheConst.OAUTH_TOKEN_DATA, authorizations[1])))) { // ---- 令牌已过期。 DataBuffer buffer = response.bufferFactory().wrap(TOKEN_EXPIRED_BYTES); return response.writeWith(Mono.just(buffer)); } // ---- 四级鉴权:判断用户类型是否与接口授权对应,否则返回相关异常,因为用户不可以 // 访问非自身类型的接口。 log.info("授权信息:{}", oauthInfo); UserTypeEnum userType = oauthInfo.getUserType(); if (!apiOauths.contains(userType.code) && !apiOauths.contains(ApiOauth.COMMON_CODE)) { DataBuffer buffer = response.bufferFactory().wrap(NO_ACCESS_AUTHORITY_BYTES); return response.writeWith(Mono.just(buffer)); } // ---- 五级鉴权:判断当前员工在当前应用下是否具有当前接口的调用权限,是则通过过滤; // 否则返回相关异常回应。如果是客户或神,直接通过过滤。因为客户没有五级鉴权,而神 // 默认拥有全部权限。 String userAccount = oauthInfo.getUserAccount(); if (ApiOauth.STAFF_CODE.equals(userType.code) && !oauthInfo.isGod()) { // ---- 如果当前员工对当前接口的调用权限不存在/已过期,返回相关异常回应。注意! // 相应许可的过期时间为null并不能直接代表不存在,因为过期时间允许为null来表示无 // 限期,因此如果相应许可的过期时间返回null,那么还需要判断相应许可是否在哈希中 // 为键的方式来确定其是无限期还是已过期。 dataKey = Cache.key(StaffsCacheConst.STAFF_PERMISSION_MAP_DATA, userAccount, appCode); String permission = appCode + SymbolEnConst.UNDERLINE + apiRewritePath; Date expireDatetime = (Date) redisTemplate.opsForHash().get(dataKey, permission); if ((expireDatetime != null && expireDatetime.before(new Date())) || !redisTemplate.opsForHash().hasKey(dataKey, permission)) { DataBuffer buffer = response.bufferFactory().wrap(AUTHORITY_EXPIRED_BYTES); return response.writeWith(Mono.just(buffer)); } } // ---- 向当前请求的请求头中置入用户ID/用户类型编码/账号,用于在业务层中获取。需要 // 通过建造者模式实现,常规的添加已经被禁止了。 ServerHttpRequest.Builder serverHttpRequestBuilder = exchange.getRequest().mutate(); serverHttpRequestBuilder.header("userId", String.valueOf(oauthInfo.getUserId())); serverHttpRequestBuilder.header("userAccount", userAccount); serverHttpRequestBuilder.header("userTypeCode", userType.code); ServerHttpRequest serverHttpRequest = serverHttpRequestBuilder.build(); exchange.mutate().request(serverHttpRequest).build(); return chain.filter(exchange); } @Override public int getOrder() { // ---- 为了获取重写后的接口路径,当前过滤器必须晚于重写过滤器执行。重写过滤器的优 // 先级大致为1,这里设置为10是比较可靠的。 return 10; } } 可以对上述代码进行改写以避免上述异常吗?
最新发布
09-03
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值