浅谈Java 三种方式实现接口校验
本文介绍了java 三种方式实现接口校验,主要包括aop,mvc拦截器,分享给大家,具体如下:
方法一: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/ */ @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/ */ @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配置
<!--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/ */ @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层我们就回填了这个参数了,这样也不会影响之前的代码逻辑(这个可能跟我的业务需求有关了)
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="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>
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。