-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a02d37f
commit 8bad78b
Showing
33 changed files
with
582 additions
and
100 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
...ava/top/sharehome/springbootinittemplate/config/encrypt/controller/EncryptController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package top.sharehome.springbootinittemplate.config.encrypt.controller; | ||
|
||
import org.springframework.context.annotation.Conditional; | ||
import org.springframework.validation.annotation.Validated; | ||
import org.springframework.web.bind.annotation.*; | ||
import top.sharehome.springbootinittemplate.common.base.R; | ||
import top.sharehome.springbootinittemplate.common.validate.PostGroup; | ||
import top.sharehome.springbootinittemplate.config.encrypt.EncryptConfiguration; | ||
import top.sharehome.springbootinittemplate.config.encrypt.annotation.RSADecrypt; | ||
import top.sharehome.springbootinittemplate.config.encrypt.annotation.RSAEncrypt; | ||
import top.sharehome.springbootinittemplate.config.encrypt.condition.EncryptCondition; | ||
import top.sharehome.springbootinittemplate.config.encrypt.controller.model.TestEncryptDto; | ||
|
||
import javax.annotation.Resource; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
/** | ||
* 加密控制器 | ||
* | ||
* @author AntonyCheng | ||
*/ | ||
@RestController("/encrypt") | ||
@Conditional(EncryptCondition.class) | ||
public class EncryptController { | ||
|
||
@Resource | ||
private EncryptConfiguration encryptConfiguration; | ||
|
||
/** | ||
* 获取RSA公钥 | ||
* | ||
* @return RSA公钥 | ||
*/ | ||
@GetMapping("/rsa/public/key") | ||
public R<String> getRsaPublicKey() { | ||
return R.ok(encryptConfiguration.getRsaPublicKey()); | ||
} | ||
|
||
/** | ||
* 加密测试控制器方法(仅测试使用,使用时取消注释GetMapping注解和RSADecrypt注解) | ||
* 注意:想要运行测试方法,需要获取RSA公钥后自行前往支持在线RSA算法加密的网站进行内容加密,再传入该方法中 | ||
*/ | ||
@PostMapping("/test") | ||
@RSADecrypt | ||
public R<Map<String, Object>> test(@RequestBody @Validated({PostGroup.class}) TestEncryptDto testEncryptDto, @RequestParam @RSAEncrypt String test) { | ||
return R.ok(new HashMap<>() { | ||
{ | ||
put("testEncryptDto", testEncryptDto); | ||
put("test", test); | ||
} | ||
}); | ||
} | ||
|
||
} |
33 changes: 33 additions & 0 deletions
33
.../top/sharehome/springbootinittemplate/config/encrypt/controller/model/TestEncryptDto.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package top.sharehome.springbootinittemplate.config.encrypt.controller.model; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Data; | ||
import lombok.NoArgsConstructor; | ||
import lombok.experimental.Accessors; | ||
import top.sharehome.springbootinittemplate.common.validate.PostGroup; | ||
import top.sharehome.springbootinittemplate.config.encrypt.annotation.RSAEncrypt; | ||
|
||
import javax.validation.constraints.Size; | ||
import java.io.Serializable; | ||
|
||
/** | ||
* 测试加密Dto类 | ||
* | ||
* @author AntonyCheng | ||
*/ | ||
@Data | ||
@AllArgsConstructor | ||
@NoArgsConstructor | ||
@Accessors(chain = true) | ||
public class TestEncryptDto implements Serializable { | ||
|
||
@Size(min = 5, max = 10, message = "账号长度介于5-10之间", groups = {PostGroup.class}) | ||
private String account; | ||
|
||
@Size(min = 5, max = 10, message = "密码长度介于5-10之间", groups = {PostGroup.class}) | ||
@RSAEncrypt | ||
private String password; | ||
|
||
private static final long serialVersionUID = 1524087173502021198L; | ||
|
||
} |
23 changes: 23 additions & 0 deletions
23
src/main/java/top/sharehome/springbootinittemplate/config/encrypt/enums/AESKeyLength.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package top.sharehome.springbootinittemplate.config.encrypt.enums; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
|
||
/** | ||
* AES密钥长度 | ||
* | ||
* @author AntonyCheng | ||
*/ | ||
@Getter | ||
@AllArgsConstructor | ||
public enum AESKeyLength { | ||
|
||
LENGTH16(16), | ||
|
||
LENGTH24(24), | ||
|
||
LENGTH32(32); | ||
|
||
private final Integer keyLength; | ||
|
||
} |
25 changes: 25 additions & 0 deletions
25
src/main/java/top/sharehome/springbootinittemplate/config/encrypt/enums/RSAKeyLength.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package top.sharehome.springbootinittemplate.config.encrypt.enums; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
|
||
/** | ||
* RSA密钥长度 | ||
* | ||
* @author AntonyCheng | ||
*/ | ||
@Getter | ||
@AllArgsConstructor | ||
public enum RSAKeyLength { | ||
|
||
LENGTH512(512), | ||
|
||
LENGTH1024(1024), | ||
|
||
LENGTH2048(2048), | ||
|
||
LENGTH4096(4096); | ||
|
||
private final Integer keyLength; | ||
|
||
} |
155 changes: 155 additions & 0 deletions
155
...ain/java/top/sharehome/springbootinittemplate/config/encrypt/filter/RSAEncryptFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
package top.sharehome.springbootinittemplate.config.encrypt.filter; | ||
|
||
import com.alibaba.fastjson.JSON; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import javax.annotation.Resource; | ||
import javax.servlet.*; | ||
import javax.servlet.annotation.WebFilter; | ||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.context.annotation.Conditional; | ||
import org.springframework.core.annotation.AnnotationUtils; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RequestParam; | ||
import org.springframework.web.method.HandlerMethod; | ||
import org.springframework.web.servlet.mvc.method.RequestMappingInfo; | ||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; | ||
import top.sharehome.springbootinittemplate.common.base.R; | ||
import top.sharehome.springbootinittemplate.common.base.ReturnCode; | ||
import top.sharehome.springbootinittemplate.config.bean.SpringContextHolder; | ||
import top.sharehome.springbootinittemplate.config.encrypt.EncryptConfiguration; | ||
import top.sharehome.springbootinittemplate.config.encrypt.annotation.RSADecrypt; | ||
import top.sharehome.springbootinittemplate.config.encrypt.annotation.RSAEncrypt; | ||
import top.sharehome.springbootinittemplate.config.encrypt.condition.EncryptCondition; | ||
import top.sharehome.springbootinittemplate.exception.customize.CustomizeEncryptException; | ||
import top.sharehome.springbootinittemplate.utils.encrypt.RSAUtils; | ||
import top.sharehome.springbootinittemplate.utils.request.ParamsAndBodyRequestWrapper; | ||
|
||
import java.io.IOException; | ||
import java.io.PrintWriter; | ||
import java.lang.annotation.Annotation; | ||
import java.lang.reflect.Field; | ||
import java.lang.reflect.Parameter; | ||
import java.util.*; | ||
|
||
/** | ||
* RSA加密过滤器 | ||
* 想要使用该过滤器,需要知道该过滤器对加密参数的识别方法: | ||
* 即先过滤所有RSADecrypt注解的Controller的URI,然后再区分RequestBody和RequestParam注解, | ||
* 如果是RequestBody,则默认是请求体,遍历其对象中所有字段,将带有RSAEncrypt注解且类型为String的字段值进行解密和更新, | ||
* 如果是RequestParam,则默认是请求参数,将带有RSAEncrypt注解且类型为String的参数值进行解密和更新。 | ||
* | ||
* @author AntonyCheng | ||
*/ | ||
@Conditional(EncryptCondition.class) | ||
@WebFilter(urlPatterns = "/*", filterName = "RSAEncryptFilter") | ||
public class RSAEncryptFilter implements Filter { | ||
|
||
@Value("${server.servlet.context-path}") | ||
private String contextPath; | ||
|
||
private final Map<String, HandlerMethod> uriMethodMap = new HashMap<>(); | ||
|
||
@Resource | ||
private EncryptConfiguration encryptConfiguration; | ||
|
||
@Override | ||
public void init(FilterConfig filterConfig) throws ServletException { | ||
// 从 Spring Context 中获取 RequestMappingHandlerMapping | ||
RequestMappingHandlerMapping requestMappingHandlerMapping = SpringContextHolder.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class); | ||
|
||
// 获取所有的 HandlerMethod 映射 | ||
Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods(); | ||
for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethods.entrySet()) { | ||
HandlerMethod handlerMethod = entry.getValue(); | ||
|
||
// 检查方法或类是否带有 @RSADecrypt 注解 | ||
if (Objects.nonNull(AnnotationUtils.findAnnotation(handlerMethod.getMethod(), RSADecrypt.class)) || | ||
Objects.nonNull(AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), RSADecrypt.class))) { | ||
|
||
// 获取 URI pattern 并将其与 HandlerMethod 关联 | ||
Set<String> patterns = entry.getKey().getPatternsCondition().getPatterns(); | ||
for (String pattern : patterns) { | ||
uriMethodMap.put(pattern, handlerMethod); | ||
} | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { | ||
// 转换参数 | ||
HttpServletRequest request = (HttpServletRequest) servletRequest; | ||
HttpServletResponse response = (HttpServletResponse) servletResponse; | ||
// 判断本次请求是否包含在RSADecrypt注解方法URI列表中,如果在,那么根据请求体注解(RequestBody)和请求参数注解(RequestParam)进行区分操作 | ||
HandlerMethod handlerMethod = uriMethodMap.get(request.getRequestURI().replace(contextPath, "")); | ||
if (Objects.nonNull(handlerMethod)) { | ||
ParamsAndBodyRequestWrapper paramsAndBodyRequestWrapper = new ParamsAndBodyRequestWrapper(request); | ||
Parameter[] parameters = handlerMethod.getMethod().getParameters(); | ||
for (Parameter parameter : parameters) { | ||
Annotation[] annotations = parameter.getAnnotations(); | ||
if (Arrays.stream(annotations).anyMatch(annotation -> annotation instanceof RequestBody)) { | ||
// 如果是 RequestBody 注解,则遍历该对象的所有 String 属性 | ||
Class<?> parameterType = parameter.getType(); | ||
Field[] declaredFields = parameterType.getDeclaredFields(); | ||
ObjectMapper objectMapper = new ObjectMapper(); | ||
Object requestBodyObject = objectMapper.readValue(paramsAndBodyRequestWrapper.getInputStream(), parameterType); | ||
for (Field declaredField : declaredFields) { | ||
RSAEncrypt annotation = declaredField.getDeclaredAnnotation(RSAEncrypt.class); | ||
if (!Objects.equals(declaredField.getType(), String.class) || Objects.isNull(annotation)) { | ||
continue; | ||
} | ||
try { | ||
declaredField.setAccessible(true); | ||
String value = (String) declaredField.get(requestBodyObject); | ||
String decrypt = RSAUtils.decrypt(value, encryptConfiguration.getRsaPrivateKey()); | ||
declaredField.set(requestBodyObject, decrypt); | ||
} catch (CustomizeEncryptException e) { | ||
response.setContentType("application/json;charset=utf-8"); | ||
response.setHeader("Access-Control-Allow-Origin", "*"); | ||
response.setHeader("Access-Control-Allow-Methods", "*"); | ||
PrintWriter writer = response.getWriter(); | ||
writer.write(JSON.toJSONString(R.fail(e.getReturnCode().getCode(), e.getMessage()))); | ||
writer.flush(); | ||
return; | ||
} catch (IllegalAccessException e) { | ||
response.setContentType("application/json;charset=utf-8"); | ||
response.setHeader("Access-Control-Allow-Origin", "*"); | ||
response.setHeader("Access-Control-Allow-Methods", "*"); | ||
PrintWriter writer = response.getWriter(); | ||
writer.write(JSON.toJSONString(R.fail(ReturnCode.FAIL.getCode(), e.getMessage()))); | ||
writer.flush(); | ||
return; | ||
} catch (Exception ignore) { | ||
} | ||
} | ||
paramsAndBodyRequestWrapper.updateOrAddRequestBody(requestBodyObject); | ||
} else if (Arrays.stream(annotations).anyMatch(annotation -> annotation instanceof RequestParam)) { | ||
RSAEncrypt annotation = parameter.getDeclaredAnnotation(RSAEncrypt.class); | ||
if (!Objects.equals(parameter.getType(), String.class) || Objects.isNull(annotation)) { | ||
continue; | ||
} | ||
String key = parameter.getName(); | ||
String value = paramsAndBodyRequestWrapper.getParameter(key); | ||
try { | ||
String decrypt = RSAUtils.decrypt(value, encryptConfiguration.getRsaPrivateKey()); | ||
paramsAndBodyRequestWrapper.updateOrAddParams(key, decrypt); | ||
} catch (CustomizeEncryptException e) { | ||
response.setContentType("application/json;charset=utf-8"); | ||
response.setHeader("Access-Control-Allow-Origin", "*"); | ||
response.setHeader("Access-Control-Allow-Methods", "*"); | ||
PrintWriter writer = response.getWriter(); | ||
writer.write(JSON.toJSONString(R.fail(e.getReturnCode().getCode(), e.getMessage()))); | ||
writer.flush(); | ||
return; | ||
} | ||
} | ||
} | ||
filterChain.doFilter(paramsAndBodyRequestWrapper, response); | ||
} else { | ||
filterChain.doFilter(request, response); | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.