Loading

REST API 基于ACCESS TOKEN 的权限解决方案

REST 设计原则是statelessness的,而且但客户端是APP时,从APP发起的请求,不是基于bowers,无法带相同的sessionid,所以比较好的方案是每次请求都带一个accesstoken进行验证。然后后台是根据token 找到用户,然后找到用户资源

但总不能每个方法都去调用token验证的方法,也不能每次验证都需要查询数据库吧!

解决办法:

  • 为了业务层只关注业务,所以需要把token验证的方法在进入controller前集中处理,用 Interceptor实现

  • 由于根据token获得用户,只需要用到 用户ID,用户登录名等 不会改变的信息,用缓存实现,需要支持过期失效,ConcurrentHashMap没有过期失效的功能,自己懒得实现就用ehcache

集中处理token

interceptor实现:

/**
* 验证token有效性
*/
@Component
public class AccessTokenVerifyInterceptor extends HandlerInterceptorAdapter {
@Resource
UserService userService;

private final static Logger LOG = LoggerFactory.getLogger(AccessTokenVerifyInterceptor.class);

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
LOG.debug("AccessTokenVerifyInterceptor executing.......");
boolean flag = false;
//accesstoken 参数
String accessToken = request.getParameter("accesstoken");
if(StringUtils.notEmpty(accessToken)) {
//验证accessToken
//verifyAccessToken 已做缓存处理
User user = userService.verifyAccessToken(accessToken);
if(user!=null){
flag = true;
//塞到request中去,供controller里面调用
request.setAttribute(SystemConstants.SESSION_NAME_USER,user);
}
}

if(!flag){
response.setStatus(HttpStatus.FORBIDDEN.value());
response.getWriter().print("wrong access token");
}
return flag;
}
}

然后到spring配置文件中加上这个拦截器:


mvc:interceptors

mvc:interceptor
<mvc:mapping path="/api/"/>
<mvc:exclude-mapping path="/
/api/user/" />
<mvc:exclude-mapping path="/
/api/accesstoken" />

</mvc:interceptor>

</mvc:interceptors>

缓存处理

pom.xml中加入ehcache包:(spring集成ehcache ,需要spring-context和spring-context-support)


net.sf.ehcache
ehcache
2.10.0

加入ehcache.xml,大部分都是默认,参考springside里面说的,改了updateCheck="false",






开启缓存,在spring配置文件中加入:



<cache:annotation-driven cache-manager="cacheManager" />










对verifyAccessToken 方法做缓存处理,也就是在原有方法上加Cacheable注解:

@Cacheable(value = "accessTokenUser",key = "#accessToken")
@Override
public User verifyAccessToken(String accessToken) {
LOG.debug("verifyAccessToken executing......");
List users = userDao.getUserByAccessToken(accessToken);
if(users.size()!=1){
if(users.size()>1){
LOG.error("accessToken 出现了重复,bug!请检查!");
}
return null;
}
return users.get(0);
}

开始run出现 java.io.NotSerializableException: cn.ifengkou.athena.model.User

User 实现序列化,再试:

前端请求三次的日志,可以看到verifyAccessToken只执行了一次

2015-12-04 15:25:56,531 INFO [cn.ifengkou.athena.controller.interceptor.AccessTokenVerifyInterceptor] -
2015-12-04 15:25:56,628 INFO [cn.ifengkou.athena.service.impl.UserServiceImpl] -
2015-12-04 15:26:21,838 INFO [cn.ifengkou.athena.controller.interceptor.AccessTokenVerifyInterceptor] -
2015-12-04 15:26:29,184 INFO [cn.ifengkou.athena.controller.interceptor.AccessTokenVerifyInterceptor] -

如有token无效,查出来User为null,cache 把null也缓存起来了

keywords:

REST,accesstoken,权限,spring,ehcache,interceptor

备注:

转载请附带原文路径:http://www.cnblogs.com/sloong/p/5157654.html

posted @ 2016-01-25 15:43  sloong  阅读(14859)  评论(6)    收藏  举报