多平台统一管理软件接口,如何实现多平台统一管理软件接口
571
2023-03-25
浅谈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配置
验证权限方法使用 直接用注解就可以了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属性加以区分
puWlzYDKblic 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版本有点出入,导致用不了基于注解的方式)
最后给出测试代码,这样的代码就优雅很多了
@RequestMapping(value = "/appointments", method = { RequestMethod.GET })
@ResponseBody
@AuthToken(type="disticntApp")
public List
List
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配置中加入
客户端:
拼接请求接口的所有参数,最后拼接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函数
token_key
md5 {{md5(str)}}
{{api.request(api.params, value0, value1, value2, value3, value4, value5, value6, value7, value8, value9)}}
{{concat(value0, value1, value2, value3, value4, value5, value6, value7, value8, value9)}}
{{api.params[0]}}
{{api.params[1]}}
{{api.params[2]}}
{{api.params[3]}}
{{api.params[4]}}
{{api.params[5]}}
{{api.params[6]}}
{{api.params[7]}}
{{api.params[8]}}
{{api.params[9]}}
token
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~