這篇文章主要介紹了淺談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;
}
#伺服器:
拼接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> <p ng-controller="APIListCtrl"> <p> 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)}} </p> <hr> <p 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> </p> </p> </body> </html>
以上是Java實作介面校驗的三種方式的詳細內容。更多資訊請關注PHP中文網其他相關文章!