Browse Source

重构国际化,网关开发。

master
caojianbin 1 year ago
parent
commit
38febe6ace
  1. 19
      ecell-internationalize/ecell-internationalize-auth/src/main/java/com/ecell/internationalize/auth/entity/UserType.java
  2. 24
      ecell-internationalize/ecell-internationalize-auth/src/main/java/com/ecell/internationalize/auth/fallback/SysUserFeignServiceFallBack.java
  3. 31
      ecell-internationalize/ecell-internationalize-auth/src/main/java/com/ecell/internationalize/auth/feign/SysUserFeignClient.java
  4. 17
      ecell-internationalize/ecell-internationalize-auth/src/main/java/com/ecell/internationalize/auth/service/SysUserLoginService.java
  5. 66
      ecell-internationalize/ecell-internationalize-auth/src/main/java/com/ecell/internationalize/auth/service/impl/SysUserLoginServiceImpl.java
  6. 123
      ecell-internationalize/ecell-internationalize-common/ecell-internationalize-core/src/main/java/com/ecell/internationalize/common/core/domain/UserLogin.java
  7. 40
      ecell-internationalize/ecell-internationalize-common/ecell-internationalize-core/src/main/java/com/ecell/internationalize/common/core/enums/UserStatus.java
  8. 108
      ecell-internationalize/ecell-internationalize-common/ecell-internationalize-security/src/main/java/com/ecell/internationalize/common/security/service/TokenService.java
  9. 28
      ecell-internationalize/ecell-internationalize-gateway/src/main/java/com/ecell/internationalize/gateway/config/RouterFunctionConfiguration.java
  10. 77
      ecell-internationalize/ecell-internationalize-gateway/src/main/java/com/ecell/internationalize/gateway/config/SwaggerProvider.java
  11. 46
      ecell-internationalize/ecell-internationalize-gateway/src/main/java/com/ecell/internationalize/gateway/config/properties/XssProperties.java
  12. 77
      ecell-internationalize/ecell-internationalize-gateway/src/main/java/com/ecell/internationalize/gateway/filter/ValidateCodeFilter.java
  13. 118
      ecell-internationalize/ecell-internationalize-gateway/src/main/java/com/ecell/internationalize/gateway/filter/XssFilter.java
  14. 38
      ecell-internationalize/ecell-internationalize-gateway/src/main/java/com/ecell/internationalize/gateway/handler/SentinelFallbackHandler.java
  15. 56
      ecell-internationalize/ecell-internationalize-gateway/src/main/java/com/ecell/internationalize/gateway/handler/SwaggerHandler.java
  16. 39
      ecell-internationalize/ecell-internationalize-gateway/src/main/java/com/ecell/internationalize/gateway/handler/ValidateCodeHandler.java
  17. 21
      ecell-internationalize/ecell-internationalize-gateway/src/main/java/com/ecell/internationalize/gateway/service/ValidateCodeService.java
  18. 116
      ecell-internationalize/ecell-internationalize-gateway/src/main/java/com/ecell/internationalize/gateway/service/impl/ValidateCodeServiceImpl.java

19
ecell-internationalize/ecell-internationalize-auth/src/main/java/com/ecell/internationalize/auth/entity/UserType.java

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
package com.ecell.internationalize.auth.entity;
import lombok.Data;
/**
* @author borui
*/
@Data
public class UserType {
/**
* 用户名
*/
private String username;
/**
* 用户密码
*/
private String password;
}

24
ecell-internationalize/ecell-internationalize-auth/src/main/java/com/ecell/internationalize/auth/fallback/SysUserFeignServiceFallBack.java

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
package com.ecell.internationalize.auth.fallback;
import com.ecell.internationalize.auth.feign.SysUserFeignClient;
import com.ecell.internationalize.common.core.domain.UserLogin;
import com.ecell.internationalize.common.core.exception.ServiceException;
import com.ecell.internationalize.common.core.utils.locale.LocaleUtil;
import net.bytebuddy.implementation.bytecode.constant.FieldConstant;
import org.springframework.stereotype.Component;
/**
* @author borui
*/
@Component
public class SysUserFeignServiceFallBack implements SysUserFeignClient {
@Override
public UserLogin queryByUser(String username) {
throw new ServiceException(LocaleUtil.getMessage("messages.fallback.info"));
}
@Override
public void update(UserLogin userLogin) {
throw new ServiceException(LocaleUtil.getMessage("messages.fallback.info"));
}
}

31
ecell-internationalize/ecell-internationalize-auth/src/main/java/com/ecell/internationalize/auth/feign/SysUserFeignClient.java

@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
package com.ecell.internationalize.auth.feign;
import com.ecell.internationalize.auth.fallback.SysUserFeignServiceFallBack;
import com.ecell.internationalize.common.core.domain.UserLogin;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
/**
* @author borui
*/
@FeignClient(value = "yisai-system-security",fallback = SysUserFeignServiceFallBack.class,contextId ="yisai-system-security003")
public interface SysUserFeignClient {
/**
* 通过用户名查询用户信息
* @param userName 用户名
* @return 结果
*/
@GetMapping("sys_user/user/queryByUserName")
UserLogin queryByUser(@RequestParam("userName") String userName);
/**
* 修改用户信息
* @param userLogin 实体对象
* @return 结果
*/
@PostMapping("sys_user/user/updateLoginStatus")
void update(@RequestBody UserLogin userLogin);
}

17
ecell-internationalize/ecell-internationalize-auth/src/main/java/com/ecell/internationalize/auth/service/SysUserLoginService.java

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
package com.ecell.internationalize.auth.service;
import com.ecell.internationalize.common.core.domain.UserLogin;
import com.ecell.internationalize.common.core.web.domain.R;
/**
* @author borui
*/
public interface SysUserLoginService {
/**
* 账号认证
* @param username 用户名
* @param password 密码
* @return UserLogin
*/
R<UserLogin> login(String username, String password);
}

66
ecell-internationalize/ecell-internationalize-auth/src/main/java/com/ecell/internationalize/auth/service/impl/SysUserLoginServiceImpl.java

@ -0,0 +1,66 @@ @@ -0,0 +1,66 @@
package com.ecell.internationalize.auth.service.impl;
import cn.hutool.http.HttpUtil;
import com.ecell.internationalize.auth.feign.SysUserFeignClient;
import com.ecell.internationalize.auth.service.SysUserLoginService;
import com.ecell.internationalize.common.core.domain.UserLogin;
import com.ecell.internationalize.common.core.enums.UserStatus;
import com.ecell.internationalize.common.core.utils.ServletUtils;
import com.ecell.internationalize.common.core.utils.StringUtils;
import com.ecell.internationalize.common.core.utils.ip.IpUtils;
import com.ecell.internationalize.common.core.utils.locale.LocaleUtil;
import com.ecell.internationalize.common.core.web.domain.R;
import com.ecell.internationalize.common.security.utils.SecurityUtils;
import net.bytebuddy.implementation.bytecode.constant.FieldConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author borui
*/
@Service
public class SysUserLoginServiceImpl implements SysUserLoginService {
private final String DELETED="0";
@Autowired
private SysUserFeignClient sysUserFeignClient;
@Override
public R<UserLogin> login(String username, String password) {
// 用户名或密码为空
if (StringUtils.isAnyBlank(username, password)) {
return R.fail(LocaleUtil.getMessage("messages.login.empty"));
}
UserLogin userInfo = sysUserFeignClient.queryByUser(username);
//查询错误
if (StringUtils.isNull(userInfo)){
return R.fail(LocaleUtil.getMessage("messages.login.error"));
}
if (!SecurityUtils.matchesPassword(password, userInfo.getPassword())){
return R.fail(LocaleUtil.getMessage("messages.error.password"));
}
//账号被删除
if (DELETED.equals(userInfo.getDelFlag())) {
return R.fail(LocaleUtil.getMessage("messages.account.delete"));
}
//账号被停用
if (UserStatus.DISABLE.getCode().equals(userInfo.getStatus())) {
return R.fail(LocaleUtil.getMessage("messages.account.delete"));
}
System.out.println("userInfo:"+userInfo.getStatus());
System.out.println("校验通过");
//修改登录时间和IP
UserLogin userLogin=new UserLogin();
userLogin.setUserId(userInfo.getUserId());
userLogin.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" );
Date date= new Date();
String str = sdf.format(date);
userLogin.setLoginDate(str);
sysUserFeignClient.update(userLogin);
return R.ok(userInfo);
}
}

123
ecell-internationalize/ecell-internationalize-common/ecell-internationalize-core/src/main/java/com/ecell/internationalize/common/core/domain/UserLogin.java

@ -0,0 +1,123 @@ @@ -0,0 +1,123 @@
package com.ecell.internationalize.common.core.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.util.Set;
/**
* @author borui
*/
@Data
public class UserLogin implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 用户唯一标识
*/
private String token;
/**
* 主键Id
*/
private String userId;
/**
* 用户昵称
*/
private String nickName;
/**
* 用户账号
*/
private String account;
/**
* 用户类型
*/
private String userType;
/**
* 用户头像
*/
private String headImg;
/**
* 密码
*/
private String password;
/**
* 帐号状态0正常 1停用
*/
private String status;
/**
* 删除标识0已删除1正常
*/
private String delFlag;
/**
* 登录IP
*/
private String loginIp;
/**
* 最后登录时间
*/
private String loginDate;
/**
* 创建人
*/
private String createUser;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 修改人
*/
private String updateUser;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/**
* 厂商Id
*/
private String firmId;
/**
* 代理商Id
*/
private String secondFirmId;
/**
* 标识(1:厂商2代理商)
*/
private String firmFlag;
/**
* 登录时间
*/
private Long loginTime;
/**
* 过期时间
*/
private Long expireTime;
/**
* 权限列表
*/
private Set<String> permissions;
/**
* 角色列表
*/
private Set<String> roles;
}

40
ecell-internationalize/ecell-internationalize-common/ecell-internationalize-core/src/main/java/com/ecell/internationalize/common/core/enums/UserStatus.java

@ -4,24 +4,24 @@ package com.ecell.internationalize.common.core.enums; @@ -4,24 +4,24 @@ package com.ecell.internationalize.common.core.enums;
* 用户状态
* @author borui
*/
public class UserStatus {
// OK("0", "正常"),DISABLE("1", "停用"),DELETED("2", "删除");
// private final String code;
// private final String info;
//
// UserStatus(String code, String info)
// {
// this.code = code;
// this.info = info;
// }
//
// public String getCode()
// {
// return code;
// }
//
// public String getInfo()
// {
// return info;
// }
public enum UserStatus {
OK("0", "正常"),DISABLE("1", "停用"),DELETED("2", "删除");
private final String code;
private final String info;
UserStatus(String code, String info)
{
this.code = code;
this.info = info;
}
public String getCode()
{
return code;
}
public String getInfo()
{
return info;
}
}

108
ecell-internationalize/ecell-internationalize-common/ecell-internationalize-security/src/main/java/com/ecell/internationalize/common/security/service/TokenService.java

@ -6,6 +6,7 @@ import com.ecell.internationalize.common.core.constant.CacheConstants; @@ -6,6 +6,7 @@ import com.ecell.internationalize.common.core.constant.CacheConstants;
import com.ecell.internationalize.common.core.constant.SecurityConstants;
import com.ecell.internationalize.common.core.domain.LoginUser;
import com.ecell.internationalize.common.core.domain.SysUser;
import com.ecell.internationalize.common.core.domain.UserLogin;
import com.ecell.internationalize.common.core.utils.JwtUtils;
import com.ecell.internationalize.common.core.utils.ServletUtils;
import com.ecell.internationalize.common.core.utils.StringUtils;
@ -13,6 +14,8 @@ import com.ecell.internationalize.common.core.utils.ip.IpUtils; @@ -13,6 +14,8 @@ import com.ecell.internationalize.common.core.utils.ip.IpUtils;
import com.ecell.internationalize.common.core.utils.uuid.IdUtils;
import com.ecell.internationalize.common.redis.service.RedisService;
import com.ecell.internationalize.common.security.utils.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@ -26,6 +29,7 @@ import java.util.concurrent.TimeUnit; @@ -26,6 +29,7 @@ import java.util.concurrent.TimeUnit;
*/
@Component
public class TokenService {
private static final Logger log = LoggerFactory.getLogger(TokenService.class);
@Autowired
private RedisService redisService;
@ -38,6 +42,7 @@ public class TokenService { @@ -38,6 +42,7 @@ public class TokenService {
private final static String ACCESS_TOKEN = CacheConstants.LOGIN_TOKEN_KEY;
private final static Long MILLIS_MINUTE_TEN = CacheConstants.REFRESH_TIME * MILLIS_MINUTE;
private static final long EXPIRATION = 720;
/**
* 创建令牌
@ -67,6 +72,34 @@ public class TokenService { @@ -67,6 +72,34 @@ public class TokenService {
}
/**
* yisai 创建令牌
* @param userLogin
* @return
*/
public Map<String, Object> createToken(UserLogin userLogin)
{
String token = IdUtils.fastUUID();
userLogin.setToken(token);
userLogin.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
refreshToken(userLogin);
// Jwt存储信息
Map<String, Object> claimsMap = new HashMap<>();
claimsMap.put(SecurityConstants.USER_KEY, token);
claimsMap.put(SecurityConstants.DETAILS_USER_ID, userLogin.getUserId());
claimsMap.put(SecurityConstants.DETAILS_USERNAME, userLogin.getAccount());
// 接口返回信息
Map<String, Object> rspMap = new HashMap<>();
rspMap.put("access_token", JwtUtils.createToken(claimsMap));
rspMap.put("expires_in",EXPIRATION);
return rspMap;
}
/**
* 获取用户身份信息
*
* @return 用户信息
@ -88,6 +121,20 @@ public class TokenService { @@ -88,6 +121,20 @@ public class TokenService {
return getLoginUser(token);
}
/**
* yisai 获取用户身份信息
*
* @return 用户信息
*/
public UserLogin getUserLogin(HttpServletRequest request)
{
// 获取请求携带的令牌
String token = SecurityUtils.getToken(request);
return getUserLogin(token);
}
/**
* 获取用户身份信息
*
@ -111,6 +158,33 @@ public class TokenService { @@ -111,6 +158,33 @@ public class TokenService {
return user;
}
/**
* 获取yisai用户身份信息
*
* @return 用户信息
*/
public UserLogin getUserLogin(String token)
{
UserLogin user = null;
try
{
if (StringUtils.isNotEmpty(token))
{
String userkey = JwtUtils.getUserKey(token);
Object cacheObject = redisService.getCacheObject(getTokenKey(userkey));
if (StringUtils.isNotNull(cacheObject)){
user= JSON.parseObject(JSON.toJSONString(cacheObject),UserLogin.class);
}
return user;
}
}
catch (Exception e)
{
}
return user;
}
/**
* 设置用户身份信息
*/
@ -149,6 +223,22 @@ public class TokenService { @@ -149,6 +223,22 @@ public class TokenService {
}
}
/**
* yisai 验证令牌有效期相差不足120分钟自动刷新缓存
*
* @param loginUser
*/
public void verifyYiSaiToken(UserLogin loginUser)
{
long expireTime = loginUser.getExpireTime();
long currentTime = System.currentTimeMillis();
if (expireTime - currentTime <= MILLIS_MINUTE_TEN)
{
refreshToken(loginUser);
}
}
/**
* 刷新令牌有效期
*
@ -163,6 +253,24 @@ public class TokenService { @@ -163,6 +253,24 @@ public class TokenService {
redisService.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
}
/**
* yisai 刷新令牌有效期
*
* @param userLogin 登录信息
*/
public void refreshToken(UserLogin userLogin)
{
userLogin.setLoginTime(System.currentTimeMillis());
userLogin.setExpireTime(userLogin.getLoginTime() + EXPIRATION * MILLIS_MINUTE);
// 根据uuid将loginUser缓存
String userKey = getTokenKey(userLogin.getToken());
redisService.setCacheObject(userKey, userLogin, EXPIRATION, TimeUnit.MINUTES);
}
private String getTokenKey(String token)
{
return ACCESS_TOKEN + token;

28
ecell-internationalize/ecell-internationalize-gateway/src/main/java/com/ecell/internationalize/gateway/config/RouterFunctionConfiguration.java

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
package com.ecell.internationalize.gateway.config;
import com.ecell.internationalize.gateway.handler.ValidateCodeHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
/**
* @author borui
*/
@Configuration
public class RouterFunctionConfiguration {
@Autowired
private ValidateCodeHandler validateCodeHandler;
@SuppressWarnings("rawtypes")
@Bean
public RouterFunction routerFunction()
{
return RouterFunctions.route(
RequestPredicates.GET("/code").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),
validateCodeHandler);
}
}

77
ecell-internationalize/ecell-internationalize-gateway/src/main/java/com/ecell/internationalize/gateway/config/SwaggerProvider.java

@ -0,0 +1,77 @@ @@ -0,0 +1,77 @@
package com.ecell.internationalize.gateway.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.support.NameUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.config.ResourceHandlerRegistry;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import java.util.ArrayList;
import java.util.List;
/**
* @author borui
*/
@Component
public class SwaggerProvider implements SwaggerResourcesProvider, WebFluxConfigurer {
/**
* Swagger2默认的url后缀
*/
public static final String SWAGGER2URL = "/v2/api-docs";
/**
* 网关路由
*/
@Lazy
@Autowired
private RouteLocator routeLocator;
@Autowired
private GatewayProperties gatewayProperties;
/**
* 聚合其他服务接口
*
* @return
*/
@Override
public List<SwaggerResource> get()
{
List<SwaggerResource> resourceList = new ArrayList<>();
List<String> routes = new ArrayList<>();
// 获取网关中配置的route
routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
gatewayProperties.getRoutes().stream()
.filter(routeDefinition -> routes
.contains(routeDefinition.getId()))
.forEach(routeDefinition -> routeDefinition.getPredicates().stream()
.filter(predicateDefinition -> "Path".equalsIgnoreCase(predicateDefinition.getName()))
.filter(predicateDefinition -> !"ecell-auth".equalsIgnoreCase(routeDefinition.getId()))
.forEach(predicateDefinition -> resourceList
.add(swaggerResource(routeDefinition.getId(), predicateDefinition.getArgs()
.get(NameUtils.GENERATED_NAME_PREFIX + "0").replace("/**", SWAGGER2URL)))));
return resourceList;
}
private SwaggerResource swaggerResource(String name, String location)
{
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion("2.0");
return swaggerResource;
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
/** swagger-ui 地址 */
registry.addResourceHandler("/swagger-ui/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");
}
}

46
ecell-internationalize/ecell-internationalize-gateway/src/main/java/com/ecell/internationalize/gateway/config/properties/XssProperties.java

@ -0,0 +1,46 @@ @@ -0,0 +1,46 @@
package com.ecell.internationalize.gateway.config.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.List;
/**
* @author borui
*/
@Configuration
@RefreshScope
@ConfigurationProperties(prefix = "security.xss")
public class XssProperties {
/**
* Xss开关
*/
private Boolean enabled;
/**
* 排除路径
*/
private List<String> excludeUrls = new ArrayList<>();
public Boolean getEnabled()
{
return enabled;
}
public void setEnabled(Boolean enabled)
{
this.enabled = enabled;
}
public List<String> getExcludeUrls()
{
return excludeUrls;
}
public void setExcludeUrls(List<String> excludeUrls)
{
this.excludeUrls = excludeUrls;
}
}

77
ecell-internationalize/ecell-internationalize-gateway/src/main/java/com/ecell/internationalize/gateway/filter/ValidateCodeFilter.java

@ -0,0 +1,77 @@ @@ -0,0 +1,77 @@
package com.ecell.internationalize.gateway.filter;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ecell.internationalize.common.core.utils.ServletUtils;
import com.ecell.internationalize.common.core.utils.StringUtils;
import com.ecell.internationalize.gateway.config.properties.CaptchaProperties;
import com.ecell.internationalize.gateway.service.ValidateCodeService;
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.core.io.buffer.DataBufferUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author borui
*/
@Component
public class ValidateCodeFilter extends AbstractGatewayFilterFactory<Object> {
private final static String[] VALIDATE_URL = new String[] { "/auth/login", "/auth/register" };
@Autowired
private ValidateCodeService validateCodeService;
@Autowired
private CaptchaProperties captchaProperties;
private static final String CODE = "code";
private static final String UUID = "uuid";
@Override
public GatewayFilter apply(Object config)
{
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
// 非登录/注册请求或验证码关闭,不处理
if (!StringUtils.containsAnyIgnoreCase(request.getURI().getPath(), VALIDATE_URL) || !captchaProperties.getEnabled())
{
return chain.filter(exchange);
}
try
{
String rspStr = resolveBodyFromRequest(request);
JSONObject obj = JSON.parseObject(rspStr);
validateCodeService.checkCaptcha(obj.getString(CODE), obj.getString(UUID));
}
catch (Exception e)
{
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), e.getMessage());
}
return chain.filter(exchange);
};
}
private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest)
{
// 获取请求体
Flux<DataBuffer> body = serverHttpRequest.getBody();
AtomicReference<String> bodyRef = new AtomicReference<>();
body.subscribe(buffer -> {
CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
DataBufferUtils.release(buffer);
bodyRef.set(charBuffer.toString());
});
return bodyRef.get();
}
}

118
ecell-internationalize/ecell-internationalize-gateway/src/main/java/com/ecell/internationalize/gateway/filter/XssFilter.java

@ -0,0 +1,118 @@ @@ -0,0 +1,118 @@
package com.ecell.internationalize.gateway.filter;
import com.ecell.internationalize.common.core.utils.StringUtils;
import com.ecell.internationalize.common.core.utils.html.EscapeUtil;
import com.ecell.internationalize.gateway.config.properties.XssProperties;
import io.netty.buffer.ByteBufAllocator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
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.*;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
/**
* @author borui
*/
@Component
@ConditionalOnProperty(value = "security.xss.enabled", havingValue = "true")
public class XssFilter implements GlobalFilter, Ordered {
// 跨站脚本的 xss 配置,nacos自行添加
@Autowired
private XssProperties xss;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
{
ServerHttpRequest request = exchange.getRequest();
// GET DELETE 不过滤
HttpMethod method = request.getMethod();
if (method == null || method == HttpMethod.GET || method == HttpMethod.DELETE)
{
return chain.filter(exchange);
}
// 非json类型,不过滤
if (!isJsonRequest(exchange))
{
return chain.filter(exchange);
}
// excludeUrls 不过滤
String url = request.getURI().getPath();
if (StringUtils.matches(url, xss.getExcludeUrls()))
{
return chain.filter(exchange);
}
ServerHttpRequestDecorator httpRequestDecorator = requestDecorator(exchange);
return chain.filter(exchange.mutate().request(httpRequestDecorator).build());
}
private ServerHttpRequestDecorator requestDecorator(ServerWebExchange exchange)
{
ServerHttpRequestDecorator serverHttpRequestDecorator = new ServerHttpRequestDecorator(exchange.getRequest())
{
@Override
public Flux<DataBuffer> getBody()
{
Flux<DataBuffer> body = super.getBody();
return body.buffer().map(dataBuffers -> {
DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
DataBuffer join = dataBufferFactory.join(dataBuffers);
byte[] content = new byte[join.readableByteCount()];
join.read(content);
DataBufferUtils.release(join);
String bodyStr = new String(content, StandardCharsets.UTF_8);
// 防xss攻击过滤
bodyStr = EscapeUtil.clean(bodyStr);
// 转成字节
byte[] bytes = bodyStr.getBytes();
NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
buffer.write(bytes);
return buffer;
});
}
@Override
public HttpHeaders getHeaders()
{
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.putAll(super.getHeaders());
// 由于修改了请求体的body,导致content-length长度不确定,因此需要删除原先的content-length
httpHeaders.remove(HttpHeaders.CONTENT_LENGTH);
httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
return httpHeaders;
}
};
return serverHttpRequestDecorator;
}
/**
* 是否是Json请求
*
* @param exchange HTTP请求
*/
public boolean isJsonRequest(ServerWebExchange exchange)
{
String header = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);
}
@Override
public int getOrder()
{
return -100;
}
}

38
ecell-internationalize/ecell-internationalize-gateway/src/main/java/com/ecell/internationalize/gateway/handler/SentinelFallbackHandler.java

@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
package com.ecell.internationalize.gateway.handler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.ecell.internationalize.common.core.utils.ServletUtils;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebExceptionHandler;
import reactor.core.publisher.Mono;
/**
* @author borui
*/
public class SentinelFallbackHandler implements WebExceptionHandler {
private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange)
{
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求超过最大数,请稍候再试");
}
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex)
{
if (exchange.getResponse().isCommitted())
{
return Mono.error(ex);
}
if (!BlockException.isBlockException(ex))
{
return Mono.error(ex);
}
return handleBlockedRequest(exchange, ex).flatMap(response -> writeResponse(response, exchange));
}
private Mono<ServerResponse> handleBlockedRequest(ServerWebExchange exchange, Throwable throwable)
{
return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable);
}
}

56
ecell-internationalize/ecell-internationalize-gateway/src/main/java/com/ecell/internationalize/gateway/handler/SwaggerHandler.java

@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
package com.ecell.internationalize.gateway.handler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import springfox.documentation.swagger.web.*;
import java.util.Optional;
/**
* @author borui
*/
@RestController
@RequestMapping("/swagger-resources")
public class SwaggerHandler {
@Autowired(required = false)
private SecurityConfiguration securityConfiguration;
@Autowired(required = false)
private UiConfiguration uiConfiguration;
private final SwaggerResourcesProvider swaggerResources;
@Autowired
public SwaggerHandler(SwaggerResourcesProvider swaggerResources)
{
this.swaggerResources = swaggerResources;
}
@GetMapping("/configuration/security")
public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration()
{
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()),
HttpStatus.OK));
}
@GetMapping("/configuration/ui")
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration()
{
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@SuppressWarnings("rawtypes")
@GetMapping("")
public Mono<ResponseEntity> swaggerResources()
{
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
}

39
ecell-internationalize/ecell-internationalize-gateway/src/main/java/com/ecell/internationalize/gateway/handler/ValidateCodeHandler.java

@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
package com.ecell.internationalize.gateway.handler;
import com.ecell.internationalize.common.core.exception.CaptchaException;
import com.ecell.internationalize.common.core.web.domain.AjaxResult;
import com.ecell.internationalize.gateway.service.ValidateCodeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import java.io.IOException;
/**
* @author borui
*/
@Component
public class ValidateCodeHandler implements HandlerFunction<ServerResponse> {
@Autowired
private ValidateCodeService validateCodeService;
@Override
public Mono<ServerResponse> handle(ServerRequest serverRequest)
{
AjaxResult ajax;
try
{
ajax = validateCodeService.createCaptcha();
}
catch (CaptchaException | IOException e)
{
return Mono.error(e);
}
return ServerResponse.status(HttpStatus.OK).body(BodyInserters.fromValue(ajax));
}
}

21
ecell-internationalize/ecell-internationalize-gateway/src/main/java/com/ecell/internationalize/gateway/service/ValidateCodeService.java

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
package com.ecell.internationalize.gateway.service;
import com.ecell.internationalize.common.core.exception.CaptchaException;
import com.ecell.internationalize.common.core.web.domain.AjaxResult;
import java.io.IOException;
/**
* @author borui
*/
public interface ValidateCodeService {
/**
* 生成验证码
*/
public AjaxResult createCaptcha() throws IOException, CaptchaException;
/**
* 校验验证码
*/
public void checkCaptcha(String key, String value) throws CaptchaException;
}

116
ecell-internationalize/ecell-internationalize-gateway/src/main/java/com/ecell/internationalize/gateway/service/impl/ValidateCodeServiceImpl.java

@ -0,0 +1,116 @@ @@ -0,0 +1,116 @@
package com.ecell.internationalize.gateway.service.impl;
import com.ecell.internationalize.common.core.constant.Constants;
import com.ecell.internationalize.common.core.exception.CaptchaException;
import com.ecell.internationalize.common.core.utils.StringUtils;
import com.ecell.internationalize.common.core.utils.sign.Base64;
import com.ecell.internationalize.common.core.utils.uuid.IdUtils;
import com.ecell.internationalize.common.core.web.domain.AjaxResult;
import com.ecell.internationalize.common.redis.service.RedisService;
import com.ecell.internationalize.gateway.config.properties.CaptchaProperties;
import com.ecell.internationalize.gateway.service.ValidateCodeService;
import com.google.code.kaptcha.Producer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.FastByteArrayOutputStream;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
/**
* @author borui
*/
@Service
public class ValidateCodeServiceImpl implements ValidateCodeService {
@Resource(name = "captchaProducer")
private Producer captchaProducer;
@Resource(name = "captchaProducerMath")
private Producer captchaProducerMath;
@Autowired
private RedisService redisService;
@Autowired
private CaptchaProperties captchaProperties;
/**
* 生成验证码
*/
@Override
public AjaxResult createCaptcha() throws IOException, CaptchaException
{
AjaxResult ajax = AjaxResult.success();
boolean captchaOnOff = captchaProperties.getEnabled();
ajax.put("captchaOnOff", captchaOnOff);
if (!captchaOnOff)
{
return ajax;
}
// 保存验证码信息
String uuid = IdUtils.simpleUUID();
String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
String capStr = null, code = null;
BufferedImage image = null;
String captchaType = captchaProperties.getType();
// 生成验证码
if ("math".equals(captchaType))
{
String capText = captchaProducerMath.createText();
capStr = capText.substring(0, capText.lastIndexOf("@"));
code = capText.substring(capText.lastIndexOf("@") + 1);
image = captchaProducerMath.createImage(capStr);
}
else if ("char".equals(captchaType))
{
capStr = code = captchaProducer.createText();
image = captchaProducer.createImage(capStr);
}
redisService.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
// 转换流信息写出
FastByteArrayOutputStream os = new FastByteArrayOutputStream();
try
{
ImageIO.write(image, "jpg", os);
}
catch (IOException e)
{
return AjaxResult.error(e.getMessage());
}
ajax.put("uuid", uuid);
ajax.put("img", Base64.encode(os.toByteArray()));
return ajax;
}
/**
* 校验验证码
*/
@Override
public void checkCaptcha(String code, String uuid) throws CaptchaException
{
if (StringUtils.isEmpty(code))
{
throw new CaptchaException("验证码不能为空");
}
if (StringUtils.isEmpty(uuid))
{
throw new CaptchaException("验证码已失效");
}
String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
String captcha = redisService.getCacheObject(verifyKey);
redisService.deleteObject(verifyKey);
if (!code.equalsIgnoreCase(captcha))
{
throw new CaptchaException("验证码错误");
}
}
}
Loading…
Cancel
Save