@ControllerAdvice 这是一个增强的 Controller。使用这个 Controller ,可以实现三个方面的功能:
全局异常处理
全局数据绑定
全局数据预处理
接口拦截器介绍
public class EncryptRequestBodyAdvice extends RequestBodyAdviceAdapter {
/**
* Invoked first to determine if this interceptor applies.
* @param methodParameter the method parameter
* @param targetType the target type, not necessarily the same as the method
* parameter type, e.g. for {@code HttpEntity<String>}.
* @param converterType the selected converter type
* @return whether this interceptor should be invoked or not
* 首先调用以确定此拦截器是否适用。 @param methodParameter 方法参数 @param targetType 目标类
* 型,不一定与方法参数类型相同,例如对于 {@code HttpEntity<String>}。 @param converterType
* 选择的转换器类型 @return 这个拦截器是否应该被调用
*/
@Override
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return false;
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
return null;
}
}
继承RequestBodyAdviceAdapter 后 重写
supports : 判断代码是否需要拦截
和
beforeBodyRead 若 supports 为true则进行 该前置方法处理
isAssignableFrom 方法介绍
public native boolean isAssignableFrom(Class<?> cls);
Determines if the class or interface represented by this {@code Class} object is either the
same as, or is a superclass or superinterface of, the class or interface represented by the
specified {@code Class} parameter. It returns {@code true} if so; otherwise it returns {@code
false}. If this {@code Class} object represents a primitive type, this method returns {@code
true} if the specified {@code Class} parameter is exactly this {@code Class} object;
otherwise it returns {@code false}.
确定此 {@code Class} 对象表示的类或接口是否与指定的 {@code Class} 参数表示的类或接口相同,或者是
其超类或超接口。如果是,则返回 {@code true};否则返回 {@code false}。如果此 {@code Class} 对象
表示原始类型,则此方法返回 {@code true} 如果指定的 {@code Class} 参数正是此 {@code Class} 对
象;否则返回 {@code false}。
Spring构造方法注入介绍
//
private final SecurityProperties securityProperties;
private final IRestEncryptHandler restEncryptHandler;
public DecryptRequestBodyAdvice(SecurityProperties securityProperties, IRestEncryptHandler restEncryptHandler) {
this.securityProperties = securityProperties;
this.restEncryptHandler = restEncryptHandler;
}
通过
EncryptAutoConfiguration @Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = SecurityProperties.PREFIX, name = "privateKey")
来判断是否需要注入
如果配置了私钥则注入
自动配置介绍
SecurityProperties类
- 指定 @ConfigurationProperties 前缀为:aizuda.security
- 如果只使用@ConfigurationProperties需要结合@EnableConfigurationProperties({SecurityProperties.class})将其注册到spring容器中。
- @Lazy (default = true )对象会在初始化的时候 不被创建
- @Configuration(proxyBeanMethods = false) proxyBeanMethods = false 是lite模式
源码中默认是true,对这个属性的解释也可以大概知道。
1: 如果为true, 则表示被@Bean标识的方法都会被CGLIB进行代理,而且会走bean的生命周期中的一些行为(比如:@PostConstruct,@Destroy等 spring中提供的生命周期), 如果bean是单例的,那么在同一个configuration中调用
@Bean标识的方法,无论调用几次得到的都是同一个bean,就是说这个bean只初始化一次。
2: 如果为false,则标识被@Bean标识的方法,不会被拦截进行CGLIB代理,也就不会走bean的生命周期中的一些行为
(比如:@PostConstruct,@Destroy等 spring中提供的生命周期),如果同一个configuration中调用@Bean标识的方
法,就只是普通方法的执行而已,并不会从容器中获取对象。所以如果单独调用@Bean标识的方法就是普通的方法调
用,而且不走bean的生命周期。
所以,如果配置类中的@Bean标识的方法之间不存在依赖调用的话,可以设置为false,可以避免拦截方法进行代理操
作,也是提升性能的一种优化。但是需要注意,@Bean标识的返回值对象还是会放入到容器中的,从容器中获取bean
还是可以是单例的,会走生命周期。
5. @Import 在导入的 {@code @Configuration} 类中声明的定义应该通过使用 { @Autowired} 注入来访问。 bean 本身
可以自动装配,或者声明 bean 的配置类实例可以自动装配。后一种方法允许在 {@code @Configuration} 类方法之间进
行显式的、IDE 友好的导航。
@Configuratio注解的类和普通bean一样正常扫描 2.@import指定导入容器的bean优先注册进容器后再进行普通bean的注册
在当前源代码中相当于 让spring 扫描到 SecurityAutoConfiguration这个类
因为 SpringFactoriesLoader载荷从和实例化给定类型的工厂“META-INF / spring.factories”文件
而当前该文件配置为
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.aizuda.security.autoconfigure.SecurityAutoConfiguration
因此若 当前 EncryptAutoConfiguration这个类 为在同包名下 则spring扫描不到 注入则失败
扩展
当前写法可为
并且在当前类上引入SecurityProperties配置类
此时 原本的SecurityAutoConfiguration类即可去掉
而该框架如此写 主要是为了看的清楚一些
- @ConditionalOnMissingBean,它是修饰bean的一个注解,主要实现的是,当你的bean被注册之后,如果而注册相同类型的bean,就不会成功,它会保证你的bean只有一个,即你的实例只有一个,当你注册多个相同的bean时,会出现异常,以此来告诉开发人员。 7. 通过@ConditionalOnProperty控制配置类是否生效,可以将配置与代码进行分离,实现了更好的控制配置.@ConditionalOnProperty实现是通过havingValue与配置文件中的值对比,返回为true则配置类生效,反之失效.8. 在 通过 resources /META-INF / spring.factories 配置成为 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.aizuda.security.autoconfigure.SecurityAutoConfiguration实现自动配置
接口解密接口
接口返回拦截对象
与请求对象相同都是两个方法
/*
* 爱组搭 http://aizuda.com 低代码组件化开发平台
* ------------------------------------------
* 受知识产权保护,请勿删除版权申明
*/
package com.aizuda.security.advice;
import com.aizuda.security.annotation.Encrypted;
import com.aizuda.security.autoconfigure.SecurityProperties;
import com.aizuda.security.handler.IRestEncryptHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/**
* 接口响应加密处理切点
* <p>
* 尊重知识产权,CV 请保留版权,爱组搭 http://aizuda.com 出品
*
* @author 青苗
* @since 2021-11-08
*/
@Slf4j
@RestControllerAdvice
public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<Object> {
private final SecurityProperties securityProperties;
private final IRestEncryptHandler restEncryptHandler;
private boolean encrypt = true;
public EncryptResponseBodyAdvice(SecurityProperties securityProperties, IRestEncryptHandler restEncryptHandler) {
this.securityProperties = securityProperties;
this.restEncryptHandler = restEncryptHandler;
}
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return Encrypted.class.isAssignableFrom(returnType.getParameterType());
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
return restEncryptHandler.response(securityProperties, body, returnType, selectedContentType,
selectedConverterType, request, response);
}
}
主要逻辑
@Override
public Object response(SecurityProperties props, Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
try {
/**
* 返回 base64 加密后的 rsa 密文内容
*/
return encryptByPrivateKey(this.toJson(body), props.getPrivateKey());
} catch (Exception e) {
log.error("RestEncryptHandler response error.", e);
}
return body;
}
/**
* 私钥加密
*
* @param plaintext 明文
* @param privateKey 私钥(BASE64编码)
* @return 返回私钥加密数据
* @throws Exception
*/
public String encryptByPrivateKey(String plaintext, String privateKey) throws Exception {
return
//先加密再转化为base64
Base64.toBase64String(RSA.encryptByPrivateKey(plaintext.getBytes(StandardCharsets.UTF_8), privateKey));
}