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))
api测试页面,用到了Bootstrap和AngularJS,还有一个js的hex_md5函数
- <!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>
 
    每天一点成长,欢迎指正!
                    
                
                
            
        
浙公网安备 33010602011771号