浅谈Java 三种方式实现接口校验

2025-05-29 0 13

本文介绍了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>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持快网idc。

原文链接:http://www.cnblogs.com/barrywxx/p/6964423.html

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 浅谈Java 三种方式实现接口校验 https://www.kuaiidc.com/114583.html

相关文章

发表评论
暂无评论