Java 三种方式实现接口校验
方法一:AOP
代码如下定义一个权限注解
- package com.thinkgem.jeesite.common.annotation;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- /**
- * 权限注解
- * Created by Hamming on 2016/12/26.
- */
- @Target(ElementType.METHOD)//这个注解是应用在方法上
- @Retention(RetentionPolicy.RUNTIME)
- public @interface AccessToken {
- /* String userId();
- String token();*/
- }
获取页面请求中的ID token
- @Aspect
- @Component
- public class AccessTokenAspect {
- @Autowired
- private ApiService apiService;
- @Around("@annotation(com.thinkgem.jeesite.common.annotation.AccessToken)")
- public Object doAccessCheck(ProceedingJoinPoint pjp) throws Throwable{
- HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
- String id = request.getParameter("id");
- String token = request.getParameter("token");
- boolean verify = apiService.verifyToken(id,token);
- if(verify){
- Object object = pjp.proceed(); //执行连接点方法
- //获取执行方法的参数
- return object;
- }else {
- return ResultApp.error(3,"token失效");
- }
- }
- }
token验证类 存储用到redis
- package com.thinkgem.jeesite.common.service;
- import com.thinkgem.jeesite.common.utils.JedisUtils;
- import io.jsonwebtoken.Jwts;
- import io.jsonwebtoken.SignatureAlgorithm;
- import io.jsonwebtoken.impl.crypto.MacProvider;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
- import org.springframework.transaction.annotation.Transactional;
- import redis.clients.jedis.Jedis;
- import java.io.*;
- import java.security.Key;
- import java.util.Date;
- /**
- *token登陆验证
- * Created by Hamming on 2016/12/23.
- */
- @Service
- public class ApiService {
- private static final String at="accessToken";
- public static Key key;
- // private Logger logger = LoggerFactory.getLogger(getClass());
- /**
- * 生成token
- * Key以字节流形式存入redis
- *
- * @param date 失效时间
- * @param appId AppId
- * @return
- */
- public String generateToken(Date date, String appId){
- Jedis jedis = null;
- try {
- jedis = JedisUtils.getResource();
- byte[] buf = jedis.get("api:key".getBytes());
- if (buf == null) { // 建新的key
- key = MacProvider.generateKey();
- ByteArrayOutputStream bao = new ByteArrayOutputStream();
- ObjectOutputStream oos = new ObjectOutputStream(bao);
- oos.writeObject(key);
- buf = bao.toByteArray();
- jedis.set("api:key".getBytes(), buf);
- } else { // 重用老key
- key = (Key) new ObjectInputStream(new ByteArrayInputStream(buf)).readObject();
- }
- }catch (IOException io){
- // System.out.println(io);
- }catch (ClassNotFoundException c){
- // System.out.println(c);
- }catch (Exception e) {
- // logger.error("ApiService", "generateToken", key, e);
- } finally {
- JedisUtils.returnResource(jedis);
- }
- String token = Jwts.builder()
- .setSubject(appId)
- .signWith(SignatureAlgorithm.HS512, key)
- .setExpiration(date)
- .compact();
- // 计算失效秒,7889400秒三个月
- Date temp = new Date();
- long interval = (date.getTime() - temp.getTime())/1000;
- JedisUtils.set(at+appId ,token,(int)interval);
- return token;
- }
- /**
- * 验证token
- * @param appId AppId
- * @param token token
- * @return
- */
- public boolean verifyToken(String appId, String token) {
- if( appId == null|| token == null){
- return false;
- }
- Jedis jedis = null;
- try {
- jedis = JedisUtils.getResource();
- if (key == null) {
- byte[] buf = jedis.get("api:key".getBytes());
- if(buf==null){
- return false;
- }
- key = (Key) new ObjectInputStream(new ByteArrayInputStream(buf)).readObject();
- }
- Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody().getSubject().equals(appId);
- return true;
- } catch (Exception e) {
- // logger.error("ApiService", "verifyToken", key, e);
- return false;
- } finally {
- JedisUtils.returnResource(jedis);
- }
- }
- /**
- * 获取token
- * @param appId
- * @return
- */
- public String getToken(String appId) {
- Jedis jedis = null;
- try {
- jedis = JedisUtils.getResource();
- return jedis.get(at+appId);
- } catch (Exception e) {
- // logger.error("ApiService", "getToken", e);
- return "";
- } finally {
- JedisUtils.returnResource(jedis);
- }
- }
- }
spring aop配置
- <!--aop -->
- <!-- 扫描注解bean -->
- <context:component-scan base-package="com.thinkgem.jeesite.common.aspect"/>
- <aop:aspectj-autoproxy proxy-target-class="true"/>
验证权限方法使用 直接用注解就可以了AccessToken
例如
- package com.thinkgem.jeesite.modules.app.web.pay;
- import com.alibaba.fastjson.JSON;
- import com.thinkgem.jeesite.common.annotation.AccessToken;
- import com.thinkgem.jeesite.common.base.ResultApp;
- import com.thinkgem.jeesite.modules.app.service.pay.AppAlipayConfService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RequestMethod;
- import org.springframework.web.bind.annotation.ResponseBody;
- import java.util.HashMap;
- import java.util.Map;
- /**
- * 支付接口
- * Created by Hamming on 2016/12/27.
- */
- @Controller
- @RequestMapping(value = "/app/pay")
- public class AppPayModule {
- @Autowired
- private AppAlipayConfService appAlipayConfService;
- @RequestMapping(value = "/alipay", method = RequestMethod.POST, produces="application/json")
- @AccessToken
- @ResponseBody
- public Object alipay(String orderId){
- if(orderId ==null){
- Map re = new HashMap<>();
- re.put("result",3);
- re.put("msg","参数错误");
- String json = JSON.toJSONString(re);
- return json;
- }else {
- return null;
- }
- }
- }
方法二: AOP方法2
- 1.定义一个查询父类,里面包含到authToken跟usedId两个属性,所有需要校验用户的请求的查询参数都继承这个查询父类,之所以会有这个userId,是因为我们校验得到用户之后,需要根据用户Id获取一些用户数据的,所以在AOP层我们就回填了这个参数了,这样也不会影响之前的代码逻辑(这个可能跟我的业务需求有关了)
public class AuthSearchVO {
public String authToken; //校验字符串
public Integer userId; //APP用户Id
public final String getAuthToken() {
return authToken;
}
public final void setAuthToken(String authToken) {
this.authToken = authToken;
}
public final Integer getUserId() {
return userId;
}
public final void setUserId(Integer userId) {
this.userId = userId;
}
@Override
public String toString() {
return "SearchVO [authToken=" + authToken + ", userId=" + userId + "]";
}
}
2.定义一个方法级的注解,所有需要校验的请求都加上这个注解,用于AOP的拦截(当然你也可以拦截所有控制器的请求)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthToken {
String type();
}3.AOP处理,之所以会将注解作为参数传进来,是因为考虑到可能会有多个APP的校验,可以利用注解的type属性加以区分
public class AuthTokenAOPInterceptor { @Resource
private AppUserService appUserService; private static final String authFieldName = "authToken";
private static final String userIdFieldName = "userId"; public void before(JoinPoint joinPoint, AuthToken authToken) throws Throwable{ Object[] args = joinPoint.getArgs(); //获取拦截方法的参数
boolean isFound = false;
for(Object arg : args){
if(arg != null){
Class<?> clazz = arg.getClass();//利用反射获取属性值
Field[] fields = clazz.getDeclaredFields();
int authIndex = -1;
int userIdIndex = -1;
for(int i = 0; i < fields.length; i++){
Field field = fields[i];
field.setAccessible(true);
if(authFieldName.equals(field.getName())){//包含校验Token
authIndex = i;
}else if(userIdFieldName.equals(field.getName())){//包含用户Id
userIdIndex = i;
}
} if(authIndex >= 0 & userIdIndex >= 0){
isFound = true;
authTokenCheck(fields[authIndex], fields[userIdIndex], arg, authToken);//校验用户
break;
}
}
}
if(!isFound){
throw new BizException(ErrorMessage.CHECK_AUTHTOKEN_FAIL);
} } private void authTokenCheck(Field authField, Field userIdField, Object arg, AuthToken authToken) throws Exception{
if(String.class == authField.getType()){
String authTokenStr = (String)authField.get(arg);//获取到校验Token
AppUser user = appUserService.getUserByAuthToken(authTokenStr);
if(user != null){
userIdField.set(arg, user.getId());
}else{
throw new BizException(ErrorMessage.CHECK_AUTHTOKEN_FAIL);
}
} }
}4.最后就是在配置文件中配置这个AOP了(因为我们的spring版本跟aspect版本有点出入,导致用不了基于注解的方式)
<bean id="authTokenAOPInterceptor" class="com.distinct.app.web.common.auth.AuthTokenAOPInterceptor"/>
<aop:config proxy-target-class="true">
<aop:pointcut id="authCheckPointcut" expression="@annotation(authToken)"/>
<aop:aspect ref="authTokenAOPInterceptor" order="1">
<aop:before method="before" pointcut-ref="authCheckPointcut"/>
</aop:aspect>
</aop:config>最后给出测试代码,这样的代码就优雅很多了
@RequestMapping(value = "/appointments", method = { RequestMethod.GET })
@ResponseBody
@AuthToken(type="disticntApp")
public List<AppointmentVo> getAppointments(AppointmentSearchVo appointmentSearchVo) {
List<AppointmentVo> appointments = appointmentService.getAppointment(appointmentSearchVo.getUserId(), appointmentSearchVo);
return appointments;
}
方法三: MVC拦截器
服务器:
拼接token之外所有参数,最后拼接token_key,做MD5,与token参数比对
如果token比对失败返回状态码 500
- public class APIInterceptor extends HandlerInterceptorAdapter {
- @Override
- public boolean preHandle(HttpServletRequest request,
- HttpServletResponse response, Object handler) throws Exception {
- Log.info(request);
- String token = request.getParameter("token");
- // token is not needed when debug
- if(token == null) return true; // !! remember to comment this when deploy on server !!
- Enumeration paraKeys = request.getParameterNames();
- String encodeStr = "";
- while (paraKeys.hasMoreElements()) {
- String paraKey = (String) paraKeys.nextElement();
- if(paraKey.equals("token"))
- break;
- String paraValue = request.getParameter(paraKey);
- encodeStr += paraValue;
- }
- encodeStr += Default.TOKEN_KEY;
- Log.out(encodeStr);
- if ( ! token.equals(DigestUtils.md5Hex(encodeStr))) {
- response.setStatus(500);
- return false;
- }
- return true;
- }
- @Override
- public void postHandle(HttpServletRequest request,
- HttpServletResponse response, Object handler,
- ModelAndView modelAndView) throws Exception {
- Log.info(request);
- }
- @Override
- public void afterCompletion(HttpServletRequest request,
- HttpServletResponse response, Object handler, Exception ex)
- throws Exception {
- }
- }
spring-config.xml配置中加入
- <mvc:interceptors>
- <mvc:interceptor>
- <mvc:mapping path="/api/*" />
- <bean class="cn.web.interceptor.APIInterceptor" />
- </mvc:interceptor>
- </mvc:interceptors>
客户端:
拼接请求接口的所有参数,最后拼接token_key,做MD5,作为token参数
请求样例:http://127.0.0.1:8080/interface/api?key0=param0&key1=param1&token=md5(concat(param0, param1))
- <!doctype html>
- <html ng-app>
- <head>
- <meta charset="UTF-8">
- <title>API test</title>
- <link href="../css/bootstrap.min.css" rel="stylesheet">
- <script src="../js/md5.min.js"></script>
- <script src="../js/angular.min.js"></script>
- <script>
- function API(url){
- this.url = arguments[0];
- this.params = Array.prototype.slice.call(arguments, 1, arguments.length);
- this.request = function(params){
- var addr = url;
- var values = Array.prototype.slice.call(arguments, 1, arguments.length);
- if(params[0] != undefined && values[0] != undefined && values[0] != '')
- addr += '?' + params[0] + "=" + values[0];
- for(var i=1; i < values.length; i++)
- if(params[i] != undefined && values[i] != undefined && values[i] != '')
- addr += "&" + params[i] + "=" + values[i];
- return addr;
- }
- }
- function APIListCtrl($scope) {
- $scope.md5 = hex_md5;
- $scope.token_key = "9ae5r06fs8";
- $scope.concat = function(){
- var args = Array.prototype.slice.call(arguments, 0, arguments.length);
- args.push($scope.token_key);
- return args.join("");
- }
- $scope.apilist = [
- new API("account/login", "username", "pwd"),
- new API("account/register", "username", "pwd", "tel", "code"),
- ] ;
- }
- </script>
- </head>
- <body>
- <div ng-controller="APIListCtrl">
- <div> Search: <input type="text" ng-model="search"><hr>
- token_key <input type="text" ng-model="token_key">
- md5 <input type="text" ng-model="str"> {{md5(str)}}
- </div>
- <hr>
- <div ng-repeat="api in apilist | filter:search" >
- <form action="{{api.url}}" method="post">
- <a href="{{api.request(api.params, value0, value1, value2, value3, value4, value5, value6, value7, value8, value9)}}">
- {{api.request(api.params, value0, value1, value2, value3, value4, value5, value6, value7, value8, value9)}}
- </a>
- <br>
- {{concat(value0, value1, value2, value3, value4, value5, value6, value7, value8, value9)}}
- <br>
- {{api.params[0]}} <input id="{{api.params[0]}}" name="{{api.params[0]}}" ng-model="value0" ng-hide="api.params[0]==undefined">
- {{api.params[1]}} <input id="{{api.params[1]}}" name="{{api.params[1]}}" ng-model="value1" ng-hide="api.params[1]==undefined">
- {{api.params[2]}} <input id="{{api.params[2]}}" name="{{api.params[2]}}" ng-model="value2" ng-hide="api.params[2]==undefined">
- {{api.params[3]}} <input id="{{api.params[3]}}" name="{{api.params[3]}}" ng-model="value3" ng-hide="api.params[3]==undefined">
- {{api.params[4]}} <input id="{{api.params[4]}}" name="{{api.params[4]}}" ng-model="value4" ng-hide="api.params[4]==undefined">
- {{api.params[5]}} <input id="{{api.params[5]}}" name="{{api.params[5]}}" ng-model="value5" ng-hide="api.params[5]==undefined">
- {{api.params[6]}} <input id="{{api.params[6]}}" name="{{api.params[6]}}" ng-model="value6" ng-hide="api.params[6]==undefined">
- {{api.params[7]}} <input id="{{api.params[7]}}" name="{{api.params[7]}}" ng-model="value7" ng-hide="api.params[7]==undefined">
- {{api.params[8]}} <input id="{{api.params[8]}}" name="{{api.params[8]}}" ng-model="value8" ng-hide="api.params[8]==undefined">
- {{api.params[9]}} <input id="{{api.params[9]}}" name="{{api.params[9]}}" ng-model="value9" ng-hide="api.params[9]==undefined">
- token <input id="token" name="token" value="{{md5(concat(value0, value1, value2, value3, value4, value5, value6, value7, value8, value9))}}">
- <input type="submit" class="btn" ng-hide="api.params[0]==undefined">
- </form>
- <hr>
- </div>
- </div>
- </body>
- </html>
Java 三种方式实现接口校验的更多相关文章
- Java三种方式实现栈和队列
栈:LIFO(后进先出) 队列:FIFO(先进先出) 1.栈:LIFO(后进先出) 1.1.栈的顺序存储结构实现: /** * 基于数组实现的顺序栈 * @param <E> */ pub ...
- 执行ANT JAVA三种方式
1. 命令行 <target name="reporttoexcel" depends="report"> <exec executable= ...
- java 多线程:Callable接口;FutureTask类实现对象【Thread、Runnable、Callable三种方式实现多线程的区别】
Callable接口介绍: Java5开始,Java提供了Callable接口,像是Runnable接口的增强版,Callable接口提供了一个 call()方法可以作为线执行体. call()方法比 ...
- 0036 Java学习笔记-多线程-创建线程的三种方式
创建线程 创建线程的三种方式: 继承java.lang.Thread 实现java.lang.Runnable接口 实现java.util.concurrent.Callable接口 所有的线程对象都 ...
- 【Java EE 学习 52】【Spring学习第四天】【Spring与JDBC】【JdbcTemplate创建的三种方式】【Spring事务管理】【事务中使用dbutils则回滚失败!!!??】
一.JDBC编程特点 静态代码+动态变量=JDBC编程. 静态代码:比如所有的数据库连接池 都实现了DataSource接口,都实现了Connection接口. 动态变量:用户名.密码.连接的数据库. ...
- Java中 实现多线程成的三种方式(继承,实现,匿名内部类)
---------------------------------------------------------------------------------------------------- ...
- java核心知识点学习----创建线程的第三种方式Callable和Future CompletionService
前面已经指出通过实现Runnable时,Thread类的作用就是将run()方法包装成线程执行体,那么是否可以直接把任意方法都包装成线程执行体呢?Java目前不行,但其模仿者C#中是可以的. Call ...
- Java Class类以及获取Class实例的三种方式
T - 由此 Class 对象建模的类的类型.例如,String.class 的类型是Class<String>.如果将被建模的类未知,则使用Class<?>. publi ...
- 【java多线程】多线程的创建三种方式--笔记
申明:线程的概念以及进程的相关概念,可以参考网络上其他资料,这里只讨论多线程是怎么实现. 一.多线程的简单理解 明白什么是多线程,小生通俗一点的理解为:在一个程序里,我想同时让这个程序完成多个任务. ...
随机推荐
- vue请求网络图片403错误,图片有占位但是显示不出来解决办法
在index.html 增加一个meta标签 <meta name="referrer" content="no-referrer" />
- Appium 测试微信小程序 Webview
通过微信打开debugx5.qq.com,或者直接扫下面二维码 勾选[打开TBS内核Inspector调试功能] Chrome查看页面元素 手机连接电脑,查看是否连接成功.如下展示设备号则为连 ...
- Python3学习笔记十八
1. MTV M: model 与数据库相关 T: Template 与html相关 V: views 与逻辑相关 一. URL配置 启动:python ...
- spring boot 集成axis1.4 java.lang.NoClassDefFoundError: Could not initialize class org.apache.axis.client.AxisClient
pom配置: <dependencies> <dependency> <groupId>org.springframework.boot</groupId&g ...
- .asmx支持post请求或者get请求调用(WebService "因 URL 意外地以 结束,请求格式无法识别" 的解决方法)
使用Post调用以asmx形式提供的webservice时,在本机调试没有调用问题.一旦部署至服务器后会提示如下信息: <html> <head> <title>因 ...
- Angular动画——路由动画及高阶动画函数
一.路由动画 路由动画需要在host元数据中指定触发器.动画注意不要过多,否则适得其反. 内容优先,引导用户去注意到某个内容.动画只是辅助手段. 定义一个进场动画,一个离场动画. 因为进场动画和离场动 ...
- 学习使人快乐6--XML
一.XML概念 Extensible Markup Language,翻译过来为可扩展标记语言.Xml技术是w3c组织发布的,目前推荐遵循的是W3C组织于2000发布的XML1.0规范. 二.学习XM ...
- jquery.string.js
/** * jquery.string - Prototype string functions for jQuery * version: 1.1.0 * (c) 2008-2011 David E ...
- 相机标定问题-Matlab & Py-Opencv
一.相机标定基本理论 1.相机成像系统介绍 图中总共有4个坐标系: 图像坐标系:Op 坐标表示方法(u,v) Unit:Dots(个) 成像坐标系:Oi ...
- mxGraph绘制流程图
代码如下: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w ...