请叫我小C的gravatar头像
请叫我小C 2018-08-22 15:32:27
SpringBoot RestFull API签名

一、需求如下

对指定的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 {

	/*
	 * 默认首页的设置,当输入域名是可以自动跳转到默认指定的网页
	 * 
	 * <p>Title: addViewControllers</p>
	 * 
	 * <p>Description: </p>
	 * 
	 * @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)  
	
	 * <p>Title: addInterceptors</p>  
	
	 * <p>Description: 拦截器配置</p>  
	
	 * @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

大功告成


打赏
最近浏览
java小书童  LV17 2021年1月8日
q847673141  LV2 2020年9月28日
EricLan  LV5 2020年6月18日
howes_hao 2020年5月18日
暂无贡献等级
756124136 2020年5月6日
暂无贡献等级
fsq647218 2020年4月24日
暂无贡献等级
18131371836  LV2 2020年3月4日
guixin  LV15 2019年12月2日
ITkuangren  LV8 2019年10月31日
xxljxgz  LV21 2019年10月14日
顶部 客服 微信二维码 底部
>扫描二维码关注最代码为好友扫描二维码关注最代码为好友