> 文档中心 > Spring boot对外接口加密访问

Spring boot对外接口加密访问


提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

Spring boot对外接口加密访问

  • 说明
  • 学习demo
  • postman测试结果
  • 总结

说明

由于公司业务需求,需要开放个别接口提供给第三方系统调用,为保证接口调用安全,请求放需要添加私钥请求校验,我方使用SHA256算法计算签名,然后进行Base64 encode,最后再进行urlEncode,来得到最终的签名,


学习demo

因为不是所有接口都需要加密访问,所以使用自定义注解来判断接口是否需要鉴权。

@Documented@Inherited@Target({ElementType.METHOD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface Auth {    boolean validate() default true;//是否需要鉴权}

因需要对接口进行过滤拦截,所以创建拦截器,通过继承HandlerInterceptorAdaper实现过滤拦截:

public class AuthSercurityInterceptor extends HandlerInterceptorAdapter {    @Value("${auth.secret.info:12345}") //双方约定秘钥    private String secret;    @Value("${auth.need:true}")  //true 表示拦截     private Boolean needAuth;    //HEADER Authorization    private static final String INFO_TIME = "timestamp";    private static final String INFO_SIGN = "sign";    private static final String AUTH_HEADER = "Authorization";    public String getSecret() { return secret;    }    public void setSecret(String secret) { this.secret = secret;    }    public Boolean getNeedAuth() { return needAuth;    }    public void setNeedAuth(Boolean needAuth) { this.needAuth = needAuth;    }    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (needAuth==null||!needAuth){     return true; } if(!handler.getClass().isAssignableFrom(HandlerMethod.class)){     return true; } Auth auth = ((HandlerMethod)handler).getMethodAnnotation(Auth.class); if (auth==null||!auth.validate()){     return true; } String authorization = request.getHeader(AUTH_HEADER); System.out.println("authorization is :" + authorization); String[] info = authorization.trim().split(","); if (info==null||info.length<1){     throw new Exception("error ....."); } String timestamp = null; String sign = null; for (int i = 0; i < info.length; i++) {     String str = info[i].trim();     if (StringUtils.isEmpty(str)){  continue;     }     String[] strSplit = str.split("=");     if (strSplit==null||strSplit.length!=2){  continue;     }     String key = strSplit[0];     String value = strSplit[1];     if (INFO_TIME.equalsIgnoreCase(key)){  timestamp = value;  System.out.println("timestamp is :" + timestamp);     }     if (INFO_SIGN.equalsIgnoreCase(key)){  sign = value;  System.out.println("sign is :" + sign);     } } if (StringUtils.isEmpty(timestamp)||StringUtils.isEmpty(sign)){     throw new Exception("error timestamp or sign is null"); } String sha256Str = SHAUtils.getSHA256Str(secret,timestamp); System.out.println("sha256str is :" + sha256Str);     if (StringUtils.isEmpty(sha256Str)){  throw new Exception("sha256Str is null ...");     }     if (!sha256Str.equals(sign)){  throw new Exception("sign error...");     }     return super.preHandle(request,response,handler); }}

然后使用SHA256算法计算签名,然后进行Base64 encoder,最后在进行urlEncoder,来得到最终签名。

public class SHAUtils {    public static final String ENCODE_TYPE_HMAC_SHA_256 ="HmacSHA256";    public static final String ENCODE_UTF_8_LOWER ="utf-8";    public static final String ENCODE_UTF_8_UPPER ="UTF-8";    public static String getSHA256Str(String secret,String message) throws Exception { if (StringUtils.isEmpty(secret)){     return null; } String encodeStr; try{     //HMAC_SHA256 加密     Mac HMAC_SHA256 = Mac.getInstance(ENCODE_TYPE_HMAC_SHA_256);     SecretKeySpec secre_spec = new SecretKeySpec(secret.getBytes(ENCODE_UTF_8_UPPER),ENCODE_TYPE_HMAC_SHA_256);     HMAC_SHA256.init(secre_spec);     byte[] bytes = HMAC_SHA256.doFinal(message.getBytes(ENCODE_UTF_8_UPPER));     if (bytes==null&&bytes.length<1){  return null;     }     //字节转换为16进制字符串     String SHA256 =byteToHex(bytes);     if (StringUtils.isEmpty(SHA256)){  return null;     }     //base64     String BASE64 = Base64.getEncoder().encodeToString(SHA256.getBytes(ENCODE_UTF_8_UPPER));     if (StringUtils.isEmpty(BASE64)){  return null;     }     //url encode     encodeStr = URLEncoder.encode(BASE64,ENCODE_UTF_8_LOWER); }catch (Exception e){     throw new Exception("get 256 info error ...."); } return encodeStr;    }    private static String byteToHex(byte[] bytes){ if (bytes==null){     return null; } StringBuffer stringBuffer = new StringBuffer(); String temp=null; for (int i = 0; i <bytes.length ; i++) {     temp = Integer.toHexString(bytes[i]&0xff);     if (temp.length()==1){  stringBuffer.append("0");     }     stringBuffer.append(temp); } return stringBuffer.toString();    }}

因为新增的自定义拦截器需要进行自定义拦截器配置

@Configurationpublic class AuthConfig extends WebMvcConfigurationSupport {    @Bean    //自定义的AuthSercurityInterceptor     public AuthSercurityInterceptor authSercurityInterceptor(){ return new AuthSercurityInterceptor();    }    @Override    //进行注册添加AuthSercurityInterceptor     protected void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authSercurityInterceptor()).addPathPatterns("/"); super.addInterceptors(registry);    }}

创建controller

@Controllerpublic class TestController {    @Auth //自定义注解    @GetMapping("/auth" )    private String myTest(){  return "Hello.....";    }}

postman测试结果

@Value("${auth.need:false}") 当为false的情况下,接口可以直接访问。
测试结果

@Value("${auth.need:true}") 当为true的情况下,我们直接访问,发现接口被拦截。
图二测试结果
当我们在请求头中加上时间戳和签名后访问,就可以访问成功!
图三测试结果
如果时间戳过期时间有要求的话,可以设置一个时间戳的最大过期时间,判断一下。

总结

刚刚接触,学习笔记,如有错误,欢迎指正。