本文介绍了Java 三种方式实现接口校验,主要包括AOP,MVC拦截器,分享给大家,具体如下:
方法一:AOP
代码如下定义一个权限注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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/ */ @Target (ElementType.METHOD) //这个注解是应用在方法上 @Retention (RetentionPolicy.RUNTIME) public @interface AccessToken { /* String userId(); String token();*/ } |
获取页面请求中的ID token
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
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/ */ @Service public class ApiService { private static final String at= "accessToken" ; public static Key key; // private Logger logger = LoggerFactorygetLogger(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配置
1
2
3
4
|
<!--aop --> <!-- 扫描注解bean --> < context:component-scan base-package = "com.thinkgem.jeesite.common.aspect" /> < aop:aspectj-autoproxy proxy-target-class = "true" /> |
验证权限方法使用 直接用注解就可以了AccessToken
例如
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
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/ */ @Controller @RequestMapping (value = "/app/pay" ) public class AppPayModule { @Autowired private AppAlipayConfService appAlipayConfService; @RequestMapping (value = "/alipay" , method = RequestMethodPOST, 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 = JSONtoJSONString(re); return json; } else { return null ; } } } |
方法二: AOP方法2
1.定义一个查询父类,里面包含到authToken跟usedId两个属性,所有需要校验用户的请求的查询参数都继承这个查询父类,之所以会有这个userId,是因为我们校验得到用户之后,需要根据用户Id获取一些用户数据的,所以在AOP层我们就回填了这个参数了,这样也不会影响之前的代码逻辑(这个可能跟我的业务需求有关了)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
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的拦截(当然你也可以拦截所有控制器的请求)
1
2
3
4
5
|
@Target (ElementType.METHOD) @Retention (RetentionPolicy.RUNTIME) public @interface AuthToken { String type(); } |
3.AOP处理,之所以会将注解作为参数传进来,是因为考虑到可能会有多个APP的校验,可以利用注解的type属性加以区分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
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版本有点出入,导致用不了基于注解的方式)
1
2
3
4
5
6
7
|
< 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 > |
最后给出测试代码,这样的代码就优雅很多了
1
2
3
4
5
6
7
|
@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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
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配置中加入
1
2
3
4
5
6
|
< 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))
api测试页面,用到了Bootstrap和AngularJS,还有一个js的hex_md5函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
<!doctype html> < html ng-app> < head > < meta charset = "UTF-8" > < title >API test</ title > < link href = "../css/bootstrap.min.css" rel = "external nofollow" 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 < valueslength ; 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)}}" rel = "external nofollow" > {{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 > |
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:http://www.cnblogs.com/barrywxx/p/6964423.html