restfull加签_SpringBoot RestFull API签名

本文介绍了如何在SpringBoot应用中实现Restful API的签名认证。通过使用JWT(Json Web Token)并自定义注解、拦截器,对特定API路径进行签名验证。详细步骤包括:理解JWT,编写自定义的`@RequireSignature`注解,创建`LoginInterceptor`拦截器进行签名验证,以及配置拦截器。
摘要由CSDN通过智能技术生成

一、需求如下

对指定的API路径进行签名认证,对于没有指定的无需认证,认证具体到方法。

二、查阅资料与开发

1.了解JWT,实际上用的开源jjwt

2.编写自定义注解

3.编写拦截器,主要是拦截特定的url进行签名验证,这里解析请求的handler是否有包含自定义注解

确定思路后,开始编写代码

A、写工具,我在网上找的,代码不复杂,一看就懂,代码如下/**

* @Title: TokenUtils.java

* @Description:

* @Copyright: Copyright (c) 2018

* @Company:http://www.sinocon.cn

* @author Administrator

* @date 2018年8月21日

* @version 1.0

*/

package cn.sinocon.hive.utils;

import java.security.Key;

import java.util.Date;

import javax.crypto.spec.SecretKeySpec;

import javax.xml.bind.DatatypeConverter;

import cn.hutool.core.date.DateUtil;

import io.jsonwebtoken.Claims;

import io.jsonwebtoken.JwtBuilder;

import io.jsonwebtoken.Jwts;

import io.jsonwebtoken.SignatureAlgorithm;

/**

* @Title: TokenUtils

* @Description:

* @author:Administrator

* @date 2018年8月21日

*/

public class TokenUtils {

/**

* 签名秘钥

*/

public static final String SECRET = "LHqDYnwpy7jzhmWdIy7EW3ER64mNlAGKRZWLKFvSKIyWWX";

/**

* 生成token

*

* @param id

* 一般传入userName

* @return

*/

public static String createJwtToken(String id) {

String issuer = "www.zuidaima.com";

String subject = "8vfu3wqEidZve2";

long ttlMillis = System.currentTimeMillis();

return createJwtToken(id, issuer, subject, ttlMillis);

}

/**

* 生成Token

*

* @param id

* 编号

* @param issuer

* 该JWT的签发者,是否使用是可选的

* @param subject

* 该JWT所面向的用户,是否使用是可选的;

* @param ttlMillis

* 签发时间

* @return token String

*/

public static String createJwtToken(String id, String issuer, String subject, long ttlMillis) {

// 签名算法 ,将对token进行签名

SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

// 生成签发时间

long nowMillis = System.currentTimeMillis();

Date now = new Date(nowMillis);

// 通过秘钥签名JWT

byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(SECRET);

Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());

// Let's set the JWT Claims

JwtBuilder builder = Jwts.builder().setId(id).setIssuedAt(now).setSubject(subject).setIssuer(issuer)

.signWith(signatureAlgorithm, signingKey);

// if it has been specified, let's add the expiration

if (ttlMillis >= 0) {

long expMillis = nowMillis + ttlMillis;

Date exp = new Date(expMillis);

builder.setExpiration(exp);

}

// Builds the JWT and serializes it to a compact, URL-safe string

return builder.compact();

}

// Sample method to validate and read the JWT

public static Claims parseJWT(String jwt) {

// This line will throw an exception if it is not a signed JWS (as

// expected)

Claims claims = Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary(SECRET)).parseClaimsJws(jwt)

.getBody();

return claims;

}

public static void main(String[] args) {

System.out.println(TokenUtils.createJwtToken("page=10"));

}

}

B、编写注解/**

* @Title: RequireSignature.java

* @Description:

* @Copyright: Copyright (c) 2018

* @Company:http://www.sinocon.cn

* @author Administrator

* @date 2018年8月18日

* @version 1.0

*/

package cn.sinocon.hive.annotation;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

/**

* @Title: RequireSignature

* @Description:

* @author:Administrator

* @date 2018年8月18日

*/

@Target({ElementType.METHOD})// 可用在方法名上

@Retention(RetentionPolicy.RUNTIME)// 运行时有效

public @interface RequireSignature {

}

C。编写拦截器/**

* @Title: LoginInterceptor.java

* @Description:

* @Copyright: Copyright (c) 2018

* @Company:http://www.sinocon.cn

* @author Administrator

* @date 2018年8月18日

* @version 1.0

*/

package cn.sinocon.hive.interceptor;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;

import org.springframework.stereotype.Component;

import org.springframework.util.ObjectUtils;

import org.springframework.web.method.HandlerMethod;

import org.springframework.web.servlet.ModelAndView;

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

import cn.hutool.core.date.DateUtil;

import cn.hutool.core.util.ObjectUtil;

import cn.sinocon.hive.annotation.RequireSignature;

import cn.sinocon.hive.utils.TokenUtils;

import io.jsonwebtoken.Claims;

/**

* @Title: LoginInterceptor

* @Description:

* @author:Administrator

* @date 2018年8月18日

*/

@Component

public class LoginInterceptor extends HandlerInterceptorAdapter {

public final static String ACCESS_TOKEN = "accessToken";

public final static String EXCEPTION_MSG = "signature does not match locally computed signature,error code:";

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)

throws Exception {

if (!(handler instanceof HandlerMethod)) {

return true;

}

HandlerMethod handlerMethod = (HandlerMethod) handler;

Method method = handlerMethod.getMethod();

RequireSignature methodAnnotation = method.getAnnotation(RequireSignature.class);

// 有 @RequireSignature 注解,需要认证

if (ObjectUtil.isNotNull(methodAnnotation)) {

// 判断是否存在令牌信息,如果存在,则允许登录

String accessToken = request.getParameter(ACCESS_TOKEN);

if (StringUtils.isBlank(accessToken)) {

// 需要认证才行

throw new RuntimeException(EXCEPTION_MSG + "400003");

}

Claims claims = null;

try {

claims = TokenUtils.parseJWT(accessToken);

} catch (Exception e) {

throw new RuntimeException(EXCEPTION_MSG + "400005");

}

// 签名格式错误,请按照约定生成签名

String[] firstParam = claims.getId().split("=");

if (ObjectUtils.isEmpty(firstParam)) {

throw new RuntimeException(EXCEPTION_MSG + "400005");

}

// 签名被篡改

String parameter = request.getParameter(firstParam[0]);

if (!firstParam[1].equals(parameter)) {

throw new RuntimeException(EXCEPTION_MSG + "400006");

}

boolean validation = false;

// 获取签名生成的时间,签名有效10分钟

try {

long timeInMillis = DateUtil.calendar(Long.parseLong(claims.get("exp") + "")).getTimeInMillis();

validation = DateUtil.calendar(System.currentTimeMillis())

.getTimeInMillis() < (timeInMillis + 10 * 60 * 1000);

} catch (Exception e) {

throw new RuntimeException(EXCEPTION_MSG + "400005");

}

// 超时

if (validation) {

throw new RuntimeException(EXCEPTION_MSG + "400007");

}

}

return super.preHandle(request, response, handler);

}

}

D。配置拦截器/**

* @Title: ResourceConfig.java

* @Description:

* @Copyright: Copyright (c) 2018

* @Company:http://www.sinocon.cn

* @author Administrator

* @date 2018年8月6日

* @version 1.0

*/

package cn.sinocon.hive.config;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.servlet.config.annotation.InterceptorRegistry;

import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;

import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import cn.sinocon.hive.interceptor.LoginInterceptor;

/**

* @Title: ResourceConfig

* @Description:

* @author:Administrator

* @date 2018年8月6日

*/

@Configuration

public class ResourceConfig implements WebMvcConfigurer {

/*

* 默认首页的设置,当输入域名是可以自动跳转到默认指定的网页

*

*

Title: addViewControllers

*

*

Description:

*

* @param registry

*

* @see org.springframework.web.servlet.config.annotation.WebMvcConfigurer#

* addViewControllers(org.springframework.web.servlet.config.annotation.

* ViewControllerRegistry)

*

*/

@Override

public void addViewControllers(ViewControllerRegistry registry) {

registry.addViewController("/").setViewName("forward:/index.html");

}

/* (non-Javadoc)

*

Title: addInterceptors

*

Description: 拦截器配置

* @param registry

* @see org.springframework.web.servlet.config.annotation.WebMvcConfigurer#addInterceptors(org.springframework.web.servlet.config.annotation.InterceptorRegistry)

*/

@Override

public void addInterceptors(InterceptorRegistry registry) {

registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/api/**");

WebMvcConfigurer.super.addInterceptors(registry);

}

}

E、在调用的controller方法中加上注解@RequireSignature

大功告成

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值