SpringBoot_Secret


SpringBoot_Secret

第一步

  • pom.xml 添加对应的架包
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>

    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.7.22</version>
    </dependency>
</dependencies>

第二步

  • 定义实体类
/**
 * 定义实体类
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String name;
    private String sex;
}

第三步

  • 自定义注解
/**
 * 自定义注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Signature {
}

第四步

  • 自定义加密工具类
/**
 * 自定义加密方法
 */
public class SignatureUtils {
    private static final String DEFAULT_SECRET = "1qaz@WSX#$%&";
    public static String sign(String body, Map<String, String[]> params, String[] paths) {
        StringBuilder sb = new StringBuilder();
        if (CharSequenceUtil.isNotBlank(body)) {
            sb.append(body).append('#');
        }

        if (!CollectionUtils.isEmpty(params)) {
            params.entrySet()
                    .stream()
                    .sorted(Map.Entry.comparingByKey())
                    .forEach(paramEntry -> {
                        String paramValue = String.join(",", Arrays.stream(paramEntry.getValue()).sorted().toArray(String[]::new));
                        sb.append(paramEntry.getKey()).append("=").append(paramValue).append('#');
                    });
        }

        if (ArrayUtil.isNotEmpty(paths)) {
            String pathValues = String.join(",", Arrays.stream(paths).sorted().toArray(String[]::new));
            sb.append(pathValues);
        }
        return SecureUtil.sha256(String.join("#", DEFAULT_SECRET, sb.toString()));
    }
}

第五步

  • 自定义 AOP 切面
/**
 * 定义 Aop 切面,对所有的参数进行按规则签名
 */
@Aspect
@Component
public class SignatureAspect {
    private static final String SIGN_HEADER = "X-SIGN";

    @Pointcut("execution(@com.example.springbootsecret.comment.Signature * *(..))")
    private void verifySignPointCut() {
    }

    @Before("verifySignPointCut()")
    public void verify() {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String sign = request.getHeader(SIGN_HEADER);

        // must have sign in header
        if (CharSequenceUtil.isBlank(sign)) {
            throw new RuntimeException("no signature in header: " + SIGN_HEADER);
        }

        // check signature
        try {
            String generatedSign = generatedSignature(request);
            if (!sign.equals(generatedSign)) {
                throw new RuntimeException("invalid signature");
            }
        } catch (Throwable throwable) {
            throw new RuntimeException("invalid signature");
        }
    }

    private String generatedSignature(HttpServletRequest request) throws IOException {
        // @RequestBody
        String bodyParam = null;
        if (request instanceof ContentCachingRequestWrapper) {
            bodyParam = new String(((ContentCachingRequestWrapper) request).getContentAsByteArray(), StandardCharsets.UTF_8);
        }

        // @RequestParam
        Map<String, String[]> requestParameterMap = request.getParameterMap();

        // @PathVariable
        String[] paths = null;
        ServletWebRequest webRequest = new ServletWebRequest(request, null);
        Map<String, String> uriTemplateVars = (Map<String, String>) webRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
        if (!CollectionUtils.isEmpty(uriTemplateVars)) {
            paths = uriTemplateVars.values().toArray(new String[0]);
        }
        return SignatureUtils.sign(bodyParam, requestParameterMap, paths);
    }
}

第六步

  • 对请求进行封装
/**
 * 对请求进行封装
 */
public class RequestFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException {
        boolean isFirstRequest = !isAsyncDispatch(request);
        HttpServletRequest requestWrapper = request;
        if (isFirstRequest && !(request instanceof ContentCachingRequestWrapper)) {
            requestWrapper = new ContentCachingRequestWrapper(request);
        }
        try {
            filterChain.doFilter(requestWrapper, response);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

第七步

  • 过滤请求
/**
 * 注册过滤
 */
@Configuration
public class SignatureConfig {

    @Bean
    public RequestFilter requestCachingFilter() {
        return new RequestFilter();
    }

    @Bean
    public FilterRegistrationBean<?> requestCachingFilterRegistration(RequestFilter requestCachingFilter) {
        FilterRegistrationBean<?> bean = new FilterRegistrationBean<>(requestCachingFilter);
        bean.setOrder(1);
        return bean;
    }
}

第八步

  • 定义请求方法
@RestController
@RequestMapping("/user")
public class UserController {

    @Signature // 方法体上要加这个注解
    @PostMapping("/{id}")
    public String myController(@PathVariable String id, @RequestParam String name, @RequestBody User user) {
        return String.join(",", id, name, user.toString());
    }
}

文章作者: L Q
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 L Q !
  目录