token验证对称加密

时间:2021-09-01
本文章向大家介绍token验证对称加密,主要包括token验证对称加密使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

一:首先建一个加密类ApiSecuritySHA 

import java.nio.charset.StandardCharsets;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;

public class ApiSecuritySHA {
    private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
    private static final String HMAC_SHA256_ALGORITHM = "HmacSHA256";

    public ApiSecuritySHA() {
    }

    public static String sha256(String key, String msg) {
        try {
            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secret_key = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
            sha256_HMAC.init(secret_key);
            byte[] array = sha256_HMAC.doFinal(msg.getBytes());
            StringBuilder sb = new StringBuilder();
            byte[] var6 = array;
            int var7 = array.length;

            for(int var8 = 0; var8 < var7; ++var8) {
                byte item = var6[var8];
                sb.append(Integer.toHexString(item & 255 | 256), 1, 3);
            }

            return sb.toString().toUpperCase();
        } catch (Exception var10) {
            throw new RuntimeException(var10);
        }
    }

    public static String sha1(String key, String data) {
        try {
            SecretKeySpec secret_key = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA1");
            Mac sha1_HMAC = Mac.getInstance("HmacSHA1");
            sha1_HMAC.init(secret_key);
            byte[] array = sha1_HMAC.doFinal(data.getBytes());
            String hash = Base64.encodeBase64String(array);
            return hash;
        } catch (Exception var6) {
            throw new RuntimeException(var6);
        }
    }
}

二:建一个拦截器进行token验证

import cn.xdf.scrm.common.cache.RedisService;
import cn.xdf.scrm.common.util.ApiSecuritySHA;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * @author yaochunhui
 * @date 2021-08-30
 * 1.使用HandlerInterceptorAdapter比直接实现HandlerInterceptor可以按需进行方法的覆盖
 * 2.拦截器和切面的执行顺序:HandlerInterceptor preHandle ==》 HandlerMethodArgumentResolver 》 业务 Method》AOP afterReturning == 》ResponseBodyAdvice beforeBodyWrite 》
 * HttpMessageConverter(转JSON )>HandlerInterceptor postHandle ==>HandlerInterceptor afterCompletion
 */
@Component
public class CheckSignInterceptor extends HandlerInterceptorAdapter {
    @Resource
    private RedisService redisService;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        /**
         * 验证签名算法:
         * 一:申请appId和secretKey
         * 应用使用者向CRM团队申请appId和secretKey。CRM团队在库里插入appId和secretKey,同事插入redis缓存
         * 二:验证签名
         * (1)headers中要含有appId、sign、ts。前端的sign由appId+secretKey+ts+参数顺序化字符串
         * (2)A.比对ts是否超时,超时直接返回false。首先验证是否超时可以避免签名计算,减少取缓存的io时间和计算签名时间。
         *     B.拿到appId在缓存中查到secretKey,查不到直接返回false。查到后用appId+secretKey+ts+body部分的参数生成sign和前端传过的sign比对
         */

        /**
         * 第一:获取header参数
         */
        Map<String, String> headerMap = new HashMap<String, String>();
        Enumeration headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key = (String) headerNames.nextElement();
            String value = request.getHeader(key);
            headerMap.put(key, value);
        }
        String appId=headerMap.get("appId");
        String sign=headerMap.get("sign");
        String ts=headerMap.get("ts");


        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        JSONObject res = new JSONObject();

        /**
         * 第二:验证参数是否为空,为空返回
         */
        if(StringUtils.isBlank(appId)||StringUtils.isBlank(sign)||StringUtils.isBlank(ts)){
            res.put("success", "false");
            res.put("code", "403");
            res.put("msg", "appId或sign或ts不能会空");
            response.getWriter().append(res.toString());
            return false;
        }

        /**
         * 第三:token失效直接返回,时间戳单位秒
         */
        Long timeDiffer=System.currentTimeMillis()/1000-Long.valueOf(ts);
        int minute=30;
        if(timeDiffer>minute*60||timeDiffer<0){
            res.put("success", "false");
            res.put("code", "403");
            res.put("msg", "签名验证超时,失效时间"+minute+"分");
            response.getWriter().append(res.toString());
            return false;
        }

        /**
         * 第四:秘钥是空说明无授权,直接返回
         */

        String secretKey=redisService.getStringValue(appId);
        if(StringUtils.isBlank(secretKey)){
            res.put("success", "false");
            res.put("code", "403");
            res.put("msg", "无授权的secretKey,请联系crm开发申请");
            response.getWriter().append(res.toString());
            return false;
        }

        String requestMethod=request.getMethod();
        String signResult="";
        if(requestMethod.equals("GET")){

            /**
             * 第五:生成sign
             */
            Map<String,String[]> parameterMap=request.getParameterMap();
            //Map<String,String[]> sortMap=map.entrySet().stream().sorted(Map.Entry.comparingByKey()).collect(Collectors.toMap(a->a.getKey(),a->a.getValue()));
            StringBuilder paramStr=new StringBuilder();
            for (Map.Entry<String,String[]> entry: parameterMap.entrySet()) {
                paramStr.append(Arrays.toString(entry.getValue()));
            }

            String signOrigin=appId+secretKey+ts+paramStr.toString();
            signResult = ApiSecuritySHA.sha256(secretKey,signOrigin);


        }else{
            /**
             * post方式要区分application/x-www-form-urlencoded和application/json。
             * x-www-form-urlencoded还是从request.getParameterMap()取数据;
             * json则用RequestWrapper包装下request
             */
            String contentType = request.getHeader("content-type");
            if (contentType.equals("application/x-www-form-urlencoded")) {
                /**
                 * 第五:生成sign
                 */
                Map<String,String[]> parameterMap=request.getParameterMap();
                StringBuilder paramStr=new StringBuilder();
                for (Map.Entry<String,String[]> entry: parameterMap.entrySet()) {
                    paramStr.append(Arrays.toString(entry.getValue()));
                }
                String signOrigin=appId+secretKey+ts+paramStr.toString();
                signResult = ApiSecuritySHA.sha256(secretKey,signOrigin);
            } else if (contentType.equals("application/json")) {
                RequestWrapper requestWrapper = new RequestWrapper(request);
                String body = requestWrapper.getBody();
                String signOrigin=appId+secretKey+ts+body;
                signResult = ApiSecuritySHA.sha256(secretKey,signOrigin);
            }

        }

        /**
         * 第六:验证sign的正确性
         */
        if(!sign.equals(signResult)){
            res.put("success", "false");
            res.put("code", "403");
            res.put("msg", "sign不正确");
            response.getWriter().append(res.toString());
            return false;
        }

        return true;
    }




}

三:配置拦截器

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;

/**
 * @author vitem
 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Resource
    private CheckSignInterceptor checkSignInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //注册TestInterceptor拦截器
        InterceptorRegistration registrationSys = registry.addInterceptor(checkSignInterceptor);
        //需要拦截的请求

        registrationSys.addPathPatterns("/**");

        //请求白名单
        registrationSys.excludePathPatterns("/swagger*/**");
        registrationSys.excludePathPatterns("/doc.html");


    }
}

四:对post的application/json进行特殊处理

4.1首先建立个RequestWrapper类 

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
public class RequestWrapper extends HttpServletRequestWrapper {
    private final String body;

    public RequestWrapper(HttpServletRequest request) {
        super(request);
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        InputStream inputStream = null;
        try {
            inputStream = request.getInputStream();
            if (inputStream != null) {
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                char[] charBuffer = new char[128];
                int bytesRead = -1;
                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                    stringBuilder.append(charBuffer, 0, bytesRead);
                }
            } else {
                stringBuilder.append("");
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        body = stringBuilder.toString();
    }
    @Override
    public ServletInputStream getInputStream() {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
        ServletInputStream servletInputStream = new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }

            @Override
            public int read() {
                return byteArrayInputStream.read();
            }
        };
        return servletInputStream;

    }
    @Override
    public BufferedReader getReader() {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }
    public String getBody() {
        return this.body;
    }
}

4.2建立一个过滤器

import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@Component
@WebFilter(urlPatterns = "/*", filterName = "channelFilter")
public class ChannelsFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        ServletRequest requestWrapper = null;
        if (servletRequest instanceof HttpServletRequest) {
            //关注点
            if (servletRequest.getContentType().equals("application/json")) {
                requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);
            }
        }
        if (requestWrapper == null) {
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            filterChain.doFilter(requestWrapper, servletResponse);
        }
    }

    @Override
    public void destroy() {
    }
}

4.3在Application启动类上加上@ServletComponentScan注解

@SpringBootApplication
@EnableSwagger2
@EnableEncryptableProperties
@EnableFeignClients
@ServletComponentScan
public class ScrmOpenApplication {

    public static void main(final String[] args) {
        SpringApplication.run(ScrmOpenApplication.class, args);
    }
}

 

五:测试

@ApiOperation(value="get header验证")
    @GetMapping(value="/scrm/header")
    public Object getHeader(String name,String password){
        return name+password;
    }

    @ApiOperation(value="post body json验证")
    @PostMapping(value="/scrm/json")
    public Object postBody(@RequestBody Student student){
        return student.getName()+student.getPassword();
    }

    @ApiOperation(value="post form urlencoded验证")
    @PostMapping(value="/scrm/form")
    public Object abc(String name,String password){
        return name+password;
    }

原文地址:https://www.cnblogs.com/yaochunhui/p/15215670.html