方法一: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. margin:auto你真的理解么?

    含义 margin:auto是具有强烈计算意味的关键字,用来计算元素对应方向应该获得的剩余空间大小 填充规则 (1) 如果一侧定值,一侧auto,则auto为剩余空间大小 (2) 如果两侧均是auto ...

  2. 键盘keyCode值

    参考地址: https://blog.csdn.net/qq_25835645/article/details/78788987

  3. 末学者笔记--apache编译安装及LAMP架构上线

    apache介绍 一.Apache的三种工作模式 Apache一共有3种稳定的MPM模式(多进程处理模块),它们分别是prefork.worker.event.http-2.2版本的httpd默认的m ...

  4. .Net Core小技巧 - Swagger适配虚拟目录及二级目录

    前言 随着前后端分离模式与微服务架构的出现,Web API变得越来越重要及普遍.而后出现的网关技术,使开发者更倾向于使用二级/多级目录来暴露Web API,一是暴露的端口更少,方便管理:二是在网关中可 ...

  5. 16 道嵌入式C语言面试题

    1. 用预处理指令#define 声明一个常数,用以表明 1 年中有多少秒(忽略闰年问题) #define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL 我在这想看到 ...

  6. 编写一份好的 Vimrc

    编写一份好的 Vimrc 目录 如何 Vimrc 色彩 空白字符与制表符 UI 配置 搜索 折叠 移动 用户自定义的前缀快捷按键 插件CtrlP 启动配置 终端Tmux 自动命令及其分组 备份 自定义 ...

  7. xpath解析html

    XPath XPath 是一门在 XML 文档中查找信息的语言.XPath 可用来在 XML 文档中对元素和属性进行遍历.XPath 是 W3C XSLT 标准的主要元素,并且 XQuery 和 XP ...

  8. java新手入门

    参考地址 java博客 1.jdk    安装 /usr/libexec/java_home -V   查询jdk的版本 2.tomcat  安装教程 配置 mkdir -p /Library/Tom ...

  9. 爬虫之scrapy-redis

    redis分布式部署 scrapy框架是否可以自己实现分布式? 不可以原因有两点 其一:因为多台机器上部署的scrapy会各自拥有各自的调度器,这样就使得多台机器无法分配start_urls列表中的u ...

  10. banner | what is the "banner" ?

    banner/横幅   获取 banner(横幅) 信息属于信息搜集   因为在 banner 信息中,可以获取到软件开发商.软件名称.服务类型.版本号等   而版本号有时候就会存在公开的 CVE 问 ...