/* * Copyright 2018-2020 by jason. All Rights Reserved. * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation, and that the name of Vinay Sajip * not be used in advertising or publicity pertaining to distribution * of the software without specific, written prior permission. * VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL * VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ package cn.mb.cloud.gateway.filter; import cn.mb.cloud.common.gateway.common.PermissionResult; import cn.mb.cloud.common.gateway.config.FilterIgnorePropertiesConfig; import cn.mb.cloud.gateway.auth.AuthService; import cn.mb.cloud.gateway.auth.ResultUtil; import cn.mb.cloud.gateway.handler.AppTypeContextHolder; import cn.mb.cloud.gateway.util.RedisUtil; import com.alibaba.fastjson.JSON; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.context.annotation.Bean; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; import org.springframework.stereotype.Component; import org.springframework.util.AntPathMatcher; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.util.*; /** * @author: jason * @Date: 2019-05-20 15:50 * @Description: */ @Slf4j @Component public class PermissionsFilter implements GlobalFilter, Ordered { @Autowired private RedisUtil redisUtil; @Autowired private FilterIgnorePropertiesConfig filterIgnorePropertiesConfig; @Autowired AuthService authService; private AntPathMatcher pathMatcher = new AntPathMatcher(); /** * 账户 */ @Value("${ignore.account.prefix}") private String IGNORE_ACCOUNT_PREFIX; /** * 账户 */ @Value("${ignore.account.uris}") private String IGNORE_ACCOUNT_URIS; /** * 福利 */ @Value("${ignore.activity.prefix}") private String IGNORE_ACTIVITY_PREFIX; /** * 福利 */ @Value("${ignore.activity.uris}") private String IGNORE_ACTIVITY_URIS; /** * 赛事 */ @Value("${ignore.competition.prefix}") private String IGNORE_COMPETITION_PREFIX; /** * 赛事 */ @Value("${ignore.competition.uris}") private String IGNORE_COMPETITION_URIS; /** * 课程 */ @Value(("${ignore.course.prefix}")) private String IGNORE_COURSE_PREFIX; /** * 课程 */ @Value(("${ignore.course.uris}")) private String IGNORE_COURSE_URIS; /** * 其他 */ @Value(("${ignore.other.prefix}")) private String IGNORE_OTHER_PREFIX; /** * 其他 */ @Value(("${ignore.other.uris}")) private String IGNORE_OTHER_URIS; /** * 世界杯 */ @Value(("${ignore.communityWorldCup.prefix}")) private String IGNORE_COMMUNITY_WORLD_CUP_PREFIX; /** * 世界杯 */ @Value(("${ignore.communityWorldCup.uris}")) private String IGNORE_COMMUNITY_WORLD_CUP_URIS; /** * 管理后台 */ @Value(("${ignore.management.prefix}")) private String IGNORE_MANAGEMENT_PREFIX; /** * 管理后台 */ @Value(("${ignore.management.uris}")) private String IGNORE_MANAGEMENT_URIS; @Override public int getOrder() { return -1; } @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse response = exchange.getResponse(); String appType = request.getHeaders().getFirst(AppTypeContextHolder.APP_TYPE); AppTypeContextHolder.setDeviceType(appType); PermissionResult permissionResult = hasPermission(exchange, request); AppTypeContextHolder.clear(); //token无效 invaild token if (permissionResult == PermissionResult.TOKEN_INVALID) { response.setRawStatusCode(200); byte[] bits = JSON.toJSONBytes(ResultUtil.tokenErr()); DataBuffer buffer = response.bufferFactory().wrap(bits); HttpHeaders headers = response.getHeaders(); String first = headers.getFirst("Content-Type"); if(StringUtils.isEmpty(first)){ headers.add("Content-Type", "application/json;charset=UTF-8"); } return response.writeWith(Mono.just(buffer)); } if (permissionResult != PermissionResult.PASS) { response.setRawStatusCode(permissionResult.getValue()); byte[] bits = JSON.toJSONBytes(permissionResult.getName()); DataBuffer buffer = response.bufferFactory().wrap(bits); HttpHeaders headers = response.getHeaders(); String first = headers.getFirst("Content-Type"); if(StringUtils.isEmpty(first)){ headers.add("Content-Type", "application/json;charset=UTF-8"); } return response.writeWith(Mono.just(buffer)); } //校验参数签名 Verify parameter signature // ResultUtil resultUtil = preHandle(request); // if(resultUtil.getCode() != 200){ // log.info("签名异常"); // response.setRawStatusCode(resultUtil.getCode()); // byte[] bits = JSON.toJSONBytes(resultUtil); // DataBuffer buffer = response.bufferFactory().wrap(bits); // HttpHeaders headers = response.getHeaders(); // String first = headers.getFirst("Content-Type"); // if(StringUtils.isEmpty(first)){ // headers.add("Content-Type", "application/json;charset=UTF-8"); // } // // return response.writeWith(Mono.just(buffer)); // } return chain.filter(exchange.mutate().request(request.mutate().build()).build()); } public ResultUtil preHandle(ServerHttpRequest req) { String uri = req.getURI().getRawPath(); if(uri.contains("v2/api-docs")){ return ResultUtil.success(); } if(uri.contains("/oauth")){ return ResultUtil.success(); } if(uri.contains("/base")){ return ResultUtil.success(); } String appid = "BT7NPhA0f775uzcUuftWjCE1TYZlWmHZ"; Map queryParams = QueryParamsUtil.getQueryParams(req); String sign = (String)queryParams.get("sign"); if(org.springframework.util.StringUtils.isEmpty(sign)){ return ResultUtil.sign(); } return authService.checkSyncAuth(appid, uri, sign, queryParams); } public PermissionResult hasPermission(ServerWebExchange exchange, ServerHttpRequest request) { String uri = request.getURI().getRawPath(); if(uri.contains("v2/api-docs")){ return PermissionResult.PASS; } String token = getAccessToken(request); //账户 if (uri.startsWith(IGNORE_ACCOUNT_PREFIX)) { if(!StringUtils.isEmpty(IGNORE_ACCOUNT_URIS)){ String[] split = IGNORE_ACCOUNT_URIS.split(","); for (String s : split) { if (uri.contains(s)) { return PermissionResult.PASS; } } } } //福利 if (uri.startsWith(IGNORE_ACTIVITY_PREFIX)) { if(!StringUtils.isEmpty(IGNORE_ACTIVITY_URIS)){ String[] split = IGNORE_ACTIVITY_URIS.split(","); for (String s : split) { if (uri.contains(s)) { return PermissionResult.PASS; } } } } //赛事 if (uri.startsWith(IGNORE_COMPETITION_PREFIX)) { if(!StringUtils.isEmpty(IGNORE_COMPETITION_URIS)){ String[] split = IGNORE_COMPETITION_URIS.split(","); for (String s : split) { if (uri.contains(s)) { return PermissionResult.PASS; } } } } //课程 if (uri.startsWith(IGNORE_COURSE_PREFIX)) { if(!StringUtils.isEmpty(IGNORE_COURSE_URIS)){ String[] split = IGNORE_COURSE_URIS.split(","); for (String s : split) { if (uri.contains(s)) { return PermissionResult.PASS; } } } } //其他 if (uri.startsWith(IGNORE_OTHER_PREFIX)) { if(!StringUtils.isEmpty(IGNORE_OTHER_URIS)){ String[] split = IGNORE_OTHER_URIS.split(","); for (String s : split) { if (uri.contains(s)) { return PermissionResult.PASS; } } } } //世界杯 if (uri.startsWith(IGNORE_COMMUNITY_WORLD_CUP_PREFIX)) { if(!StringUtils.isEmpty(IGNORE_COMMUNITY_WORLD_CUP_URIS)){ String[] split = IGNORE_COMMUNITY_WORLD_CUP_URIS.split(","); for (String s : split) { if (uri.contains(s)) { return PermissionResult.PASS; } } } } // ----------------------------------------------------------------- 放行综合后台授权登录-UPMS if (uri.startsWith(IGNORE_MANAGEMENT_PREFIX)) { if(!StringUtils.isEmpty(IGNORE_MANAGEMENT_URIS)){ String[] split = IGNORE_MANAGEMENT_URIS.split(","); for (String s : split) { if (uri.contains(s)) { return PermissionResult.PASS; } } } String value = redisUtil.getValue(token.substring(token.length() - 32)); if(StringUtils.isEmpty(value)){ log.warn("Redis Token Store未找到对应的令牌,accessToken:" + token); return PermissionResult.TOKEN_INVALID; } return PermissionResult.PASS; // String accessToken = getAccessToken(request); // if (StringUtils.isEmpty(accessToken)) { // log.warn("访问令牌为空,accessToken:" + accessToken); // return PermissionResult.TOKEN_INVALID; // } // // boolean hasAuth = LoginHelper.userExist(accessToken); // if (!hasAuth) { // log.warn("Redis Token Store未找到对应的令牌,accessToken:" + accessToken); // return PermissionResult.TOKEN_INVALID; // } else { // if (!LoginHelper.isExpired(LoginHelper.getUser(accessToken))) { // log.warn("访问令牌已过期,accessToken:" + accessToken); // return PermissionResult.TOKEN_EXPIRED; // } // LoginHelper.update(LoginHelper.getUser(accessToken), 86400000L); // return PermissionResult.PASS; // } } // 判断是否是忽略列表中的 //Check if it is in the ignore list. if (!filterIgnorePropertiesConfig.getPermissionUrls().isEmpty()) { Optional optional = filterIgnorePropertiesConfig.getPermissionUrls().stream().filter(url -> pathMatcher.match(url, uri)).findFirst(); if (optional.isPresent()) { return PermissionResult.PASS; } } if (StringUtils.isEmpty(token)) { log.warn("访问令牌为空,accessToken:" + token); return PermissionResult.TOKEN_INVALID; } // OAuth2AccessToken oAuth2AccessToken = redisTokenStore.readAccessToken(token); // if (oAuth2AccessToken == null) { // log.warn("Redis Token Store未找到对应的令牌,accessToken:" + token); // return PermissionResult.TOKEN_INVALID; // } // if (oAuth2AccessToken.isExpired()) { // log.warn("访问令牌已过期,accessToken:" + token); // return PermissionResult.TOKEN_EXPIRED; // } // Long userId = (Long) oAuth2AccessToken.getAdditionalInformation().get("user_id"); // String username = (String) oAuth2AccessToken.getAdditionalInformation().get("username"); // //用户令牌有效则进行用户信息的注入到头信息中 // request.mutate().header("userId", userId.toString()).header("username", username); // //是否开启简单认证模式如果开启则不验证URL权限只验证是否拥有token的合法性 // if (isBasic) { // return PermissionResult.PASS; // } // HashSet permissions = (HashSet) redisTemplate.opsForValue().get(SecurityConstants.CACHE_USER_PERMISSIONS_KEY + "urls:" + userId); // if (permissions == null || permissions.isEmpty()) { // log.warn("用户URL权限为空,urlSet:" + JSON.toJSONString(permissions)); // return PermissionResult.FORBIDDEN; // } // Optional optional = permissions.stream().filter(url -> pathMatcher.match(url, uri)).findFirst(); String value = redisUtil.getValue(token.substring(token.length() - 32)); if(StringUtils.isEmpty(value)){ log.warn("Redis Token Store未找到对应的令牌,accessToken:" + token); return PermissionResult.TOKEN_INVALID; } return PermissionResult.PASS; } /** * 获取请求中token * * @param request * @return token */ private String getAccessToken(ServerHttpRequest request) { String headerValue = request.getHeaders().getFirst("Authorization"); if (StringUtils.isEmpty(headerValue)) { return null; } final String bearerType = OAuth2AccessToken.BEARER_TYPE.toLowerCase(); if (headerValue.toLowerCase().startsWith(bearerType)) { String substring = headerValue.substring(bearerType.length()); return substring.trim(); } else { return headerValue; } } }