REST API 基于ACCESS TOKEN 的權限解決方案

MaxPco 9年前發布 | 52K 次閱讀 Ehcache REST API WEB服務/RPC/SOA

來自: http://www.cnblogs.com/sloong/p/5157654.html

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;
    

    } }</pre>

    然后到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></pre>

    緩存處理

    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></pre> 
    

    加入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></pre>

    開啟緩存,在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></pre>

對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

備注:

</div>

 本文由用戶 MaxPco 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!