方法一:AOP

代码如下定义一个权限注解

  1. package com.thinkgem.jeesite.common.annotation;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. /**
  7. * 权限注解
  8. * Created by Hamming on 2016/12/26.
  9. */
  10. @Target(ElementType.METHOD)//这个注解是应用在方法上
  11. @Retention(RetentionPolicy.RUNTIME)
  12. public @interface AccessToken {
  13. /*    String userId();
  14. String token();*/
  15. }

获取页面请求中的ID token

  1. @Aspect
  2. @Component
  3. public class AccessTokenAspect {
  4. @Autowired
  5. private ApiService apiService;
  6. @Around("@annotation(com.thinkgem.jeesite.common.annotation.AccessToken)")
  7. public Object doAccessCheck(ProceedingJoinPoint pjp) throws Throwable{
  8. HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
  9. String id = request.getParameter("id");
  10. String token = request.getParameter("token");
  11. boolean verify = apiService.verifyToken(id,token);
  12. if(verify){
  13. Object object = pjp.proceed(); //执行连接点方法
  14. //获取执行方法的参数
  15. return object;
  16. }else {
  17. return ResultApp.error(3,"token失效");
  18. }
  19. }
  20. }

token验证类  存储用到redis

  1. package com.thinkgem.jeesite.common.service;
  2. import com.thinkgem.jeesite.common.utils.JedisUtils;
  3. import io.jsonwebtoken.Jwts;
  4. import io.jsonwebtoken.SignatureAlgorithm;
  5. import io.jsonwebtoken.impl.crypto.MacProvider;
  6. import org.slf4j.Logger;
  7. import org.slf4j.LoggerFactory;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.stereotype.Service;
  10. import org.springframework.transaction.annotation.Transactional;
  11. import redis.clients.jedis.Jedis;
  12. import java.io.*;
  13. import java.security.Key;
  14. import java.util.Date;
  15. /**
  16. *token登陆验证
  17. * Created by Hamming on 2016/12/23.
  18. */
  19. @Service
  20. public class ApiService {
  21. private static final String at="accessToken";
  22. public static Key key;
  23. //    private Logger logger = LoggerFactory.getLogger(getClass());
  24. /**
  25. * 生成token
  26. * Key以字节流形式存入redis
  27. *
  28. * @param date  失效时间
  29. * @param appId AppId
  30. * @return
  31. */
  32. public String generateToken(Date date, String appId){
  33. Jedis jedis = null;
  34. try {
  35. jedis = JedisUtils.getResource();
  36. byte[] buf = jedis.get("api:key".getBytes());
  37. if (buf == null) { // 建新的key
  38. key = MacProvider.generateKey();
  39. ByteArrayOutputStream bao = new ByteArrayOutputStream();
  40. ObjectOutputStream oos = new ObjectOutputStream(bao);
  41. oos.writeObject(key);
  42. buf = bao.toByteArray();
  43. jedis.set("api:key".getBytes(), buf);
  44. } else { // 重用老key
  45. key = (Key) new ObjectInputStream(new ByteArrayInputStream(buf)).readObject();
  46. }
  47. }catch (IOException io){
  48. //            System.out.println(io);
  49. }catch (ClassNotFoundException c){
  50. //            System.out.println(c);
  51. }catch (Exception e) {
  52. //            logger.error("ApiService", "generateToken", key, e);
  53. } finally {
  54. JedisUtils.returnResource(jedis);
  55. }
  56. String token = Jwts.builder()
  57. .setSubject(appId)
  58. .signWith(SignatureAlgorithm.HS512, key)
  59. .setExpiration(date)
  60. .compact();
  61. // 计算失效秒,7889400秒三个月
  62. Date temp = new Date();
  63. long interval = (date.getTime() - temp.getTime())/1000;
  64. JedisUtils.set(at+appId ,token,(int)interval);
  65. return token;
  66. }
  67. /**
  68. * 验证token
  69. * @param appId AppId
  70. * @param token token
  71. * @return
  72. */
  73. public boolean verifyToken(String appId, String token) {
  74. if( appId == null|| token == null){
  75. return false;
  76. }
  77. Jedis jedis = null;
  78. try {
  79. jedis = JedisUtils.getResource();
  80. if (key == null) {
  81. byte[] buf = jedis.get("api:key".getBytes());
  82. if(buf==null){
  83. return false;
  84. }
  85. key = (Key) new ObjectInputStream(new ByteArrayInputStream(buf)).readObject();
  86. }
  87. Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody().getSubject().equals(appId);
  88. return true;
  89. } catch (Exception e) {
  90. //            logger.error("ApiService", "verifyToken", key, e);
  91. return false;
  92. } finally {
  93. JedisUtils.returnResource(jedis);
  94. }
  95. }
  96. /**
  97. * 获取token
  98. * @param appId
  99. * @return
  100. */
  101. public String getToken(String appId) {
  102. Jedis jedis = null;
  103. try {
  104. jedis = JedisUtils.getResource();
  105. return jedis.get(at+appId);
  106. } catch (Exception e) {
  107. //            logger.error("ApiService", "getToken", e);
  108. return "";
  109. } finally {
  110. JedisUtils.returnResource(jedis);
  111. }
  112. }
  113. }

spring aop配置

  1. <!--aop -->
  2. <!--      扫描注解bean -->
  3. <context:component-scan base-package="com.thinkgem.jeesite.common.aspect"/>
  4. <aop:aspectj-autoproxy proxy-target-class="true"/>

验证权限方法使用 直接用注解就可以了AccessToken

例如

    1. package com.thinkgem.jeesite.modules.app.web.pay;
    2. import com.alibaba.fastjson.JSON;
    3. import com.thinkgem.jeesite.common.annotation.AccessToken;
    4. import com.thinkgem.jeesite.common.base.ResultApp;
    5. import com.thinkgem.jeesite.modules.app.service.pay.AppAlipayConfService;
    6. import org.springframework.beans.factory.annotation.Autowired;
    7. import org.springframework.stereotype.Controller;
    8. import org.springframework.web.bind.annotation.RequestMapping;
    9. import org.springframework.web.bind.annotation.RequestMethod;
    10. import org.springframework.web.bind.annotation.ResponseBody;
    11. import java.util.HashMap;
    12. import java.util.Map;
    13. /**
    14. * 支付接口
    15. * Created by Hamming on 2016/12/27.
    16. */
    17. @Controller
    18. @RequestMapping(value = "/app/pay")
    19. public class AppPayModule {
    20. @Autowired
    21. private AppAlipayConfService appAlipayConfService;
    22. @RequestMapping(value = "/alipay", method = RequestMethod.POST, produces="application/json")
    23. @AccessToken
    24. @ResponseBody
    25. public Object alipay(String orderId){
    26. if(orderId ==null){
    27. Map re = new HashMap<>();
    28. re.put("result",3);
    29. re.put("msg","参数错误");
    30. String json = JSON.toJSONString(re);
    31. return json;
    32. }else {
    33. return null;
    34. }
    35. }
    36. }

方法二: 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

  1. public class APIInterceptor extends HandlerInterceptorAdapter {
  2. @Override
  3. public boolean preHandle(HttpServletRequest request,
  4. HttpServletResponse response, Object handler) throws Exception {
  5. Log.info(request);
  6. String token = request.getParameter("token");
  7. // token is not needed when debug
  8. if(token == null) return true;  // !! remember to comment this when deploy on server !!
  9. Enumeration paraKeys = request.getParameterNames();
  10. String encodeStr = "";
  11. while (paraKeys.hasMoreElements()) {
  12. String paraKey = (String) paraKeys.nextElement();
  13. if(paraKey.equals("token"))
  14. break;
  15. String paraValue = request.getParameter(paraKey);
  16. encodeStr += paraValue;
  17. }
  18. encodeStr += Default.TOKEN_KEY;
  19. Log.out(encodeStr);
  20. if ( ! token.equals(DigestUtils.md5Hex(encodeStr))) {
  21. response.setStatus(500);
  22. return false;
  23. }
  24. return true;
  25. }
  26. @Override
  27. public void postHandle(HttpServletRequest request,
  28. HttpServletResponse response, Object handler,
  29. ModelAndView modelAndView) throws Exception {
  30. Log.info(request);
  31. }
  32. @Override
  33. public void afterCompletion(HttpServletRequest request,
  34. HttpServletResponse response, Object handler, Exception ex)
  35. throws Exception {
  36. }
  37. }

spring-config.xml配置中加入

  1. <mvc:interceptors>
  2. <mvc:interceptor>
  3. <mvc:mapping path="/api/*" />
  4. <bean class="cn.web.interceptor.APIInterceptor" />
  5. </mvc:interceptor>
  6. </mvc:interceptors>

客户端:

拼接请求接口的所有参数,最后拼接token_key,做MD5,作为token参数

请求样例:http://127.0.0.1:8080/interface/api?key0=param0&key1=param1&token=md5(concat(param0, param1))

 
api测试页面,用到了Bootstrap和AngularJS,还有一个js的hex_md5函数
  1. <!doctype html>
  2. <html ng-app>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>API test</title>
  6. <link href="../css/bootstrap.min.css" rel="stylesheet">
  7. <script src="../js/md5.min.js"></script>
  8. <script src="../js/angular.min.js"></script>
  9. <script>
  10. function API(url){
  11. this.url = arguments[0];
  12. this.params = Array.prototype.slice.call(arguments, 1, arguments.length);
  13. this.request = function(params){
  14. var addr = url;
  15. var values = Array.prototype.slice.call(arguments, 1, arguments.length);
  16. if(params[0] != undefined && values[0] != undefined && values[0] != '')
  17. addr += '?' + params[0] + "=" + values[0];
  18. for(var i=1; i < values.length; i++)
  19. if(params[i] != undefined && values[i] != undefined && values[i] != '')
  20. addr += "&" + params[i] + "=" + values[i];
  21. return addr;
  22. }
  23. }
  24. function APIListCtrl($scope) {
  25. $scope.md5 = hex_md5;
  26. $scope.token_key = "9ae5r06fs8";
  27. $scope.concat = function(){
  28. var args = Array.prototype.slice.call(arguments, 0, arguments.length);
  29. args.push($scope.token_key);
  30. return args.join("");
  31. }
  32. $scope.apilist = [
  33. new API("account/login", "username", "pwd"),
  34. new API("account/register", "username", "pwd", "tel", "code"),
  35. ] ;
  36. }
  37. </script>
  38. </head>
  39. <body>
  40. <div ng-controller="APIListCtrl">
  41. <div> Search: <input type="text" ng-model="search"><hr>
  42. token_key <input type="text" ng-model="token_key">
  43. md5 <input type="text" ng-model="str"> {{md5(str)}}
  44. </div>
  45. <hr>
  46. <div ng-repeat="api in apilist | filter:search" >
  47. <form action="{{api.url}}" method="post">
  48. <a href="{{api.request(api.params, value0, value1, value2, value3, value4, value5, value6, value7, value8, value9)}}">
  49. {{api.request(api.params, value0, value1, value2, value3, value4, value5, value6, value7, value8, value9)}}
  50. </a>
  51. <br>
  52. {{concat(value0, value1, value2, value3, value4, value5, value6, value7, value8, value9)}}
  53. <br>
  54. {{api.params[0]}} <input id="{{api.params[0]}}" name="{{api.params[0]}}" ng-model="value0" ng-hide="api.params[0]==undefined">
  55. {{api.params[1]}} <input id="{{api.params[1]}}" name="{{api.params[1]}}" ng-model="value1" ng-hide="api.params[1]==undefined">
  56. {{api.params[2]}} <input id="{{api.params[2]}}" name="{{api.params[2]}}" ng-model="value2" ng-hide="api.params[2]==undefined">
  57. {{api.params[3]}} <input id="{{api.params[3]}}" name="{{api.params[3]}}" ng-model="value3" ng-hide="api.params[3]==undefined">
  58. {{api.params[4]}} <input id="{{api.params[4]}}" name="{{api.params[4]}}" ng-model="value4" ng-hide="api.params[4]==undefined">
  59. {{api.params[5]}} <input id="{{api.params[5]}}" name="{{api.params[5]}}" ng-model="value5" ng-hide="api.params[5]==undefined">
  60. {{api.params[6]}} <input id="{{api.params[6]}}" name="{{api.params[6]}}" ng-model="value6" ng-hide="api.params[6]==undefined">
  61. {{api.params[7]}} <input id="{{api.params[7]}}" name="{{api.params[7]}}" ng-model="value7" ng-hide="api.params[7]==undefined">
  62. {{api.params[8]}} <input id="{{api.params[8]}}" name="{{api.params[8]}}" ng-model="value8" ng-hide="api.params[8]==undefined">
  63. {{api.params[9]}} <input id="{{api.params[9]}}" name="{{api.params[9]}}" ng-model="value9" ng-hide="api.params[9]==undefined">
  64. token <input id="token" name="token" value="{{md5(concat(value0, value1, value2, value3, value4, value5, value6, value7, value8, value9))}}">
  65. <input type="submit" class="btn" ng-hide="api.params[0]==undefined">
  66. </form>
  67. <hr>
  68. </div>
  69. </div>
  70. </body>
  71. </html>

Java 三种方式实现接口校验的更多相关文章

  1. Java三种方式实现栈和队列

    栈:LIFO(后进先出) 队列:FIFO(先进先出) 1.栈:LIFO(后进先出) 1.1.栈的顺序存储结构实现: /** * 基于数组实现的顺序栈 * @param <E> */ pub ...

  2. 执行ANT JAVA三种方式

    1. 命令行 <target name="reporttoexcel" depends="report"> <exec executable= ...

  3. java 多线程:Callable接口;FutureTask类实现对象【Thread、Runnable、Callable三种方式实现多线程的区别】

    Callable接口介绍: Java5开始,Java提供了Callable接口,像是Runnable接口的增强版,Callable接口提供了一个 call()方法可以作为线执行体. call()方法比 ...

  4. 0036 Java学习笔记-多线程-创建线程的三种方式

    创建线程 创建线程的三种方式: 继承java.lang.Thread 实现java.lang.Runnable接口 实现java.util.concurrent.Callable接口 所有的线程对象都 ...

  5. 【Java EE 学习 52】【Spring学习第四天】【Spring与JDBC】【JdbcTemplate创建的三种方式】【Spring事务管理】【事务中使用dbutils则回滚失败!!!??】

    一.JDBC编程特点 静态代码+动态变量=JDBC编程. 静态代码:比如所有的数据库连接池 都实现了DataSource接口,都实现了Connection接口. 动态变量:用户名.密码.连接的数据库. ...

  6. Java中 实现多线程成的三种方式(继承,实现,匿名内部类)

    ---------------------------------------------------------------------------------------------------- ...

  7. java核心知识点学习----创建线程的第三种方式Callable和Future CompletionService

    前面已经指出通过实现Runnable时,Thread类的作用就是将run()方法包装成线程执行体,那么是否可以直接把任意方法都包装成线程执行体呢?Java目前不行,但其模仿者C#中是可以的. Call ...

  8. Java Class类以及获取Class实例的三种方式

    T - 由此 Class 对象建模的类的类型.例如,String.class 的类型是Class<String>.如果将被建模的类未知,则使用Class<?>.   publi ...

  9. 【java多线程】多线程的创建三种方式--笔记

    申明:线程的概念以及进程的相关概念,可以参考网络上其他资料,这里只讨论多线程是怎么实现. 一.多线程的简单理解 明白什么是多线程,小生通俗一点的理解为:在一个程序里,我想同时让这个程序完成多个任务. ...

随机推荐

  1. vue请求网络图片403错误,图片有占位但是显示不出来解决办法

    在index.html 增加一个meta标签 <meta name="referrer" content="no-referrer" />

  2. Appium 测试微信小程序 Webview

    通过微信打开debugx5.qq.com,或者直接扫下面二维码   勾选[打开TBS内核Inspector调试功能]   Chrome查看页面元素 手机连接电脑,查看是否连接成功.如下展示设备号则为连 ...

  3. Python3学习笔记十八

    1.    MTV M:   model     与数据库相关 T:   Template    与html相关 V:   views      与逻辑相关 一.    URL配置 启动:python ...

  4. 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 ...

  5. .asmx支持post请求或者get请求调用(WebService "因 URL 意外地以 结束,请求格式无法识别" 的解决方法)

    使用Post调用以asmx形式提供的webservice时,在本机调试没有调用问题.一旦部署至服务器后会提示如下信息: <html> <head> <title>因 ...

  6. Angular动画——路由动画及高阶动画函数

    一.路由动画 路由动画需要在host元数据中指定触发器.动画注意不要过多,否则适得其反. 内容优先,引导用户去注意到某个内容.动画只是辅助手段. 定义一个进场动画,一个离场动画. 因为进场动画和离场动 ...

  7. 学习使人快乐6--XML

    一.XML概念 Extensible Markup Language,翻译过来为可扩展标记语言.Xml技术是w3c组织发布的,目前推荐遵循的是W3C组织于2000发布的XML1.0规范. 二.学习XM ...

  8. jquery.string.js

    /** * jquery.string - Prototype string functions for jQuery * version: 1.1.0 * (c) 2008-2011 David E ...

  9. 相机标定问题-Matlab & Py-Opencv

    一.相机标定基本理论 1.相机成像系统介绍 图中总共有4个坐标系: 图像坐标系:Op    坐标表示方法(u,v)                 Unit:Dots(个) 成像坐标系:Oi      ...

  10. mxGraph绘制流程图

    代码如下: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w ...