18 changed files with 1024 additions and 20 deletions
@ -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; |
||||
} |
@ -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")); |
||||
} |
||||
} |
@ -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); |
||||
|
||||
} |
@ -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); |
||||
} |
@ -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); |
||||
} |
||||
} |
@ -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; |
||||
|
||||
|
||||
} |
@ -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); |
||||
} |
||||
} |
@ -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/"); |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
@ -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(); |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
@ -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))); |
||||
} |
||||
|
||||
} |
@ -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)); |
||||
} |
||||
} |
@ -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; |
||||
} |
@ -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…
Reference in new issue