aizuda 学习之 @ControllerAdvice 和RequestBodyAdviceAdapter运用 自动配置介绍

@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类

  1. 指定 @ConfigurationProperties 前缀为:aizuda.security
  2. 如果只使用@ConfigurationProperties需要结合@EnableConfigurationProperties({SecurityProperties.class})将其注册到spring容器中。
  3. @Lazy (default = true )对象会在初始化的时候 不被创建
  4. @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类即可去掉

而该框架如此写 主要是为了看的清楚一些

  1. @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));
    }
  • 14
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值