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>
<!--API ACCESS TOKEN INTERCEPTOR-->
<mvc:interceptor>
<mvc:mapping path="/api/**"/>
<mvc:exclude-mapping path="/**/api/user/**" />
<mvc:exclude-mapping path="/**/api/accesstoken" />
<bean class="cn.ifengkou.athena.controller.interceptor.AccessTokenVerifyInterceptor"></bean>
</mvc:interceptor>
<!--other interceptor -->
</mvc:interceptors>
缓存处理
pom.xml中加入ehcache包:(spring集成ehcache ,需要spring-context和spring-context-support)
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.0</version>
</dependency>
加入ehcache.xml,大部分都是默认,参考springside里面说的,改了updateCheck="false",
<ehcache updateCheck="false"
monitoring="autodetect"
dynamicConfig="true">
<diskStore path="java.io.tmpdir" />
<cache name="accessTokenUser"
maxEntriesLocalHeap="10000"
maxEntriesLocalDisk="1000"
eternal="false"
diskSpoolBufferSizeMB="20"
timeToIdleSeconds="300" timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LFU"
transactionalMode="off">
<persistence strategy="localTempSwap" />
</cache>
</ehcache>
开启缓存,在spring配置文件中加入:
<!-- 缓存配置 -->
<!-- 启用缓存注解功能(请将其配置在Spring主配置文件中) -->
<cache:annotation-driven cache-manager="cacheManager" />
<!-- Spring自己的基于java.util.concurrent.ConcurrentHashMap实现的缓存管理器(该功能是从Spring3.1开始提供的) -->
<!-- <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches"> <set> <bean name="myCache" class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"/>
</set> </property> </bean> -->
<!-- 若只想使用Spring自身提供的缓存器,则注释掉下面的两个关于Ehcache配置的bean,并启用上面的SimpleCacheManager即可 -->
<!-- Spring提供的基于的Ehcache实现的缓存管理器 -->
<bean id="cacheManagerFactory"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache.xml" />
</bean>
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="cacheManagerFactory" />
</bean>
对verifyAccessToken 方法做缓存处理,也就是在原有方法上加Cacheable注解:
@Cacheable(value = "accessTokenUser",key = "#accessToken")
@Override
public User verifyAccessToken(String accessToken) {
LOG.debug("verifyAccessToken executing......");
List<User> 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] - <AccessTokenVerifyInterceptor executing.......>
2015-12-04 15:25:56,628 INFO [cn.ifengkou.athena.service.impl.UserServiceImpl] - <verifyAccessToken executing......>
2015-12-04 15:26:21,838 INFO [cn.ifengkou.athena.controller.interceptor.AccessTokenVerifyInterceptor] - <AccessTokenVerifyInterceptor executing.......>
2015-12-04 15:26:29,184 INFO [cn.ifengkou.athena.controller.interceptor.AccessTokenVerifyInterceptor] - <AccessTokenVerifyInterceptor executing.......>
如有token无效,查出来User为null,cache 把null也缓存起来了
keywords:
REST,accesstoken,权限,spring,ehcache,interceptor
备注:
转载请附带原文路径:http://www.cnblogs.com/sloong/p/5157654.html
REST API 基于ACCESS TOKEN 的权限解决方案的更多相关文章
- REST API 基于ACCESS TOKEN
REST API 基于ACCESS TOKEN 的权限解决方案 REST 设计原则是statelessness的,而且但客户端是APP时,从APP发起的请求,不是基于bowers,无法带相同的se ...
- 使用Azure Rest API获得Access Token介绍
背景 本文主要介绍如何获取如何获取Azure Rest API的访问token,所采用的是v2.0版本的Microsoft标识平台,关于1.0和2.0的区别可以参考 https://docs.azur ...
- ASP.NET Core 实战:基于 Jwt Token 的权限控制全揭露
一.前言 在涉及到后端项目的开发中,如何实现对于用户权限的管控是需要我们首先考虑的,在实际开发过程中,我们可能会运用一些已经成熟的解决方案帮助我们实现这一功能,而在 Grapefruit.VuCore ...
- 怎样用Google APIs和Google的应用系统进行集成(4)----获得Access Token以通过一些Google APIs的OAuth2认证
在上篇文章中: "怎样用Google APIs和Google的应用系统进行集成(3)----调用发现Google APIs的RESTful的服务"一文中,我们直接用jdk的java ...
- SharePoint Online 使用 adal js 获取access token
最近在写一些SharePoint 的sample code, 有兴趣的小伙伴可以查看我的GitHub. 今天给大家介绍SharePoint Framework (SPFx )web part 当中怎 ...
- Web API与OAuth:既生access token,何生refresh token
在前一篇博文中,我们基于 ASP.NET Web API 与 OWIN OAuth 以 Resource Owner Password Credentials Grant 的授权方式( grant_t ...
- 微信开发-ACCESS TOKEN 过期失效解决方案
微信开发-ACCESS TOKEN 过期失效解决方案 起因:因为access_token的重要性,开发过微信的都知道,但是他有自己的生命周期,官方解释为:"有效期为7200秒",一 ...
- 基于.Net Framework 4.0 Web API开发(4):ASP.NET Web APIs 基于令牌TOKEN验证的实现
概述: ASP.NET Web API 的好用使用过的都知道,没有复杂的配置文件,一个简单的ApiController加上需要的Action就能工作.但是在使用API的时候总会遇到跨域请求的问题, ...
- vue基于d2-admin的RBAC权限管理解决方案
前两篇关于vue权限路由文章的填坑,说了一堆理论,是时候操作一波了. vue权限路由实现方式总结 vue权限路由实现方式总结二 选择d2-admin是因为element-ui的相关开源项目里,d2-a ...
随机推荐
- CentOS 下如何查看并清理系统内存空间
有时候在服务器上打开了很多会占用内存的程序但关闭这些程序后,发现内存空间还是和没有关闭应用程序时的占用一样,以致使其它应用程序打开时内存不够或很卡,那么此时就想清理掉以前的程序打开时所占用的内存.而大 ...
- php header 函数详解
网页的缓存是由 HTTP消息头中的“Cache-control”来控制的,常见的取值有private.no-cache.max-age.must- revalidate等,默认为private.其作用 ...
- java8新特性笔记
1.forEach(),遍历数据结构中的元素,括号内可以带一个闭包的方法 2.双冒号用法:forEach(this::doSchedule),如果运行环境是闭包,java允许使用双冒号的写法来直接调用 ...
- ContactsContract.CommonDataKinds【Translated By KillerLegend】
http://developer.android.com/reference/android/provider/ContactsContract.CommonDataKinds.html interf ...
- flask程序部署在openshift上的一些注意事项
https://www.openshift.com/blogs/how-to-install-and-configure-a-python-flask-dev-environment-deploy-t ...
- 微信分享朋友链接显示js代码
通常自己做的一个页面想通过微信像朋友分享时,展示的标题和描述都是不是自己想要的,自己查了一些资料,原来是通过js来进行控制 展示效果如下: 标题.描述.还有分享的图片都是有js来控制的. js代码如下 ...
- 如何实现GridView的选中,编辑,取消,删除功能
protected void GridView1_RowDeleting(object sender, GridViewDeleteEventArgs e) { string sqlstr = &qu ...
- 一个.net程序员教你使用less
我是一个.net 程序员,虽然说一直做后台,但是web 前端也会去学,虽然说技术只是层窗户纸,但是像我这种多动症患者,不捅破我心难受啊! 好!废话不多提,下面直接正题,至于less 是什么这里不多讲因 ...
- python djange输入中文错误的解决办法
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128) ...
- Android实现入门界面布局
Android实现入门界面布局 开发工具:Andorid Studio 1.3 运行环境:Android 4.4 KitKat 代码实现 首先是常量的定义,安卓中固定字符串应该定义在常量中. stri ...