请求校验可以保证数据通信时的安全性:
@Inherited
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PublicUrl {
/**
* 是否校验token合法性
*
* @return
*/
boolean tokenValidate() default false;
/**
* 是否校验签名合法性
*
* @return
*/
boolean signValidate() default false;
}
@EnableWebMvc
@Configuration
public class MyAppConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getHandlerInterceptor());
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedHeaders("Content-Type", "x-requested-with", "X-Custom-Header")
.allowedMethods("PUT", "POST", "GET", "DELETE", "OPTIONS")
.allowedOrigins("*")
.allowCredentials(true);
}
@Bean
public HandlerInterceptor getHandlerInterceptor() {
return new SafeHandlerInterceptor();
}
}
public class SafeHandlerInterceptor implements HandlerInterceptor {
@Autowired
private MyProperties properties;
@Autowired
private UserDao userDao;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (properties.getTokenDisable()) {
return true;
}
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
if (handlerMethod.getBean() instanceof BasicErrorController) {//exclude frame BasicErrorController
return true;
}
ValidateResponse validateResponse = new ValidateResponse(true, null);
//validate controller method annotation to judge token or params whether or not validate
PublicUrl publicUrl = handlerMethod.getMethodAnnotation(PublicUrl.class);
if (null != publicUrl) {
if (publicUrl.signValidate() == true) {
validateResponse = paramSignValidate(request, response);
}
if (validateResponse.isValidate() && publicUrl.tokenValidate() == true) {
validateResponse = tokenValidate(request, response);
}
} else {
//validate controller class annotation to judge token or params whether or not validate
publicUrl = handlerMethod.getBean().getClass().getAnnotation(PublicUrl.class);
if (null != publicUrl) {
if (publicUrl.signValidate() == true) {
validateResponse = paramSignValidate(request, response);
}
if (validateResponse.isValidate() && publicUrl.tokenValidate() == true) {
validateResponse = tokenValidate(request, response);
}
} else {
validateResponse = validate(request, response);
}
}
if (!validateResponse.isValidate()) {
throw validateResponse.getException();
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { }
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { }
/**
* 全局校验
*
* @param request
* @param response
* @return
*/
private ValidateResponse validate(HttpServletRequest request, HttpServletResponse response) {
ValidateResponse validateResponse = paramSignValidate(request, response);
if (validateResponse.isValidate()) {
validateResponse = tokenValidate(request, response);
}
return validateResponse;
}
/**
* 签名校验
*
* @param request
* @param response
* @return
*/
private ValidateResponse paramSignValidate(HttpServletRequest request, HttpServletResponse response) {
String sign = request.getParameter("sign");
Map<String, String[]> map = request.getParameterMap();
// 将参数按照一定规则获取到sign和前端传过来的sign进行比较,规则自定义需要和前端一致
String sign1 = SignUtil.signMap(request.getServletPath(), map);
if (ObjectUtil.isEmpty(sign) || !sign.equals(sign1)) {
return new ValidateResponse(false, new ApiException(ResultEnums.ILLEGAL_REQUEST));
}
return new ValidateResponse(true, null);
}
/**
* token校验
*
* @param request
* @param response
* @return
*/
private ValidateResponse tokenValidate(HttpServletRequest request, HttpServletResponse response) {
String token = request.getParameter(“token”);
if (ObjectUtil.isEmpty(token)) {
return new ValidateResponse(false, new ApiException(ResultEnums.MISS_TOKEN));
}
String userUuid = request.getParameter(USER_UUID);
// 此处将token保存在了db,也可以放在redis
User user = userDao.selectByUuid(userUuid);
if (ObjectUtil.isEmpty(user)) {
return new ValidateResponse(false, new ApiException(ResultEnums.ILLEGAL_USER));
}
// 将前台传来的token和服务器的进行比较,前端的token可以是用户登录时由一定规则生成的一个密钥,随返回值一起传给前端存储
if (!token.equals(user.getToken())) {
return new ValidateResponse(false, new ApiException(ResultEnums.ERROR_TOKEN));
}
return new ValidateResponse(true, null);
}
/**
* 校验返回对象
*/
private static class ValidateResponse {
private boolean validate;
private YkApiException exception;
public ValidateResponse(boolean validate, YkApiException exception) {
this.validate = validate;
this.exception = exception;
}
public boolean isValidate() {
return validate;
}
public Exception getException() {
return exception;
}
}
}
public class SignUtil {
private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
private final static String randomBaseStr = "abcdefghijklmnopqrstuvwxyz0123456789";
private static String SIGN_KEY = "123456"; // 自定义
private static String CHARSET_NAME = "UTF-8";
private static final String sign = "sign";
public static String signMap(String path, Map<String, String[]> map) {
StringBuilder param = new StringBuilder(path);
Iterator<Map.Entry<String, String[]>> entries = map.entrySet().iterator();
Map<String, String[]> params = new TreeMap<>();
while (entries.hasNext()) {
Map.Entry<String, String[]> entry = entries.next();
String key = entry.getKey();
String[] values = entry.getValue();
if (values == null) {
entries.remove();
}
if (sign.equals(key)) {
continue;
}
params.put(key, values);
}
Iterator<Map.Entry<String, String[]>> entries2 = params.entrySet().iterator();
while (entries2.hasNext()) {
Map.Entry<String, String[]> entry = entries2.next();
String key = entry.getKey();
String[] values = entry.getValue();
if (values.length == 1) {
param.append("&").append(key).append("=").append(values[0]);
} else {
param.append("&").append(key).append("=").append(JsonUtil.toJson(values));
}
}
System.out.println(param);
return MD5Encode(param.toString() + SIGN_KEY, CHARSET_NAME);
}
private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++)
resultSb.append(byteToHexString(b[i]));
return resultSb.toString();
}
private static String byteToHexString(byte b) {
int n = b;
if (n < 0)
n += 256;
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
public static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (charsetname == null || "".equals(charsetname))
resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
else
resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
} catch (Exception exception) {
}
return resultString;
}
}
@PostMapping(value = "/testToken")
@PublicUrl(tokenValidate = true)
public Result<Boolean> testToken(String token) {
return ResultUtil.success(。。。);
}
最后的最后,博主也是个java新手,记录下工作中的好东西,有什么不对或者还可以提高的地方,欢迎大家留言指正!!!