1. 程式人生 > >Java移動端後臺之單點登入

Java移動端後臺之單點登入

1.需求分析

    單點登入在軟體開發中是一個老生常談的話題;以筆者公司的後臺專案為例,筆者公司的app是以航母戰鬥群的形式開發的,一個後臺必須供四五個app訪問,甚至更多;並且分app中還集成了第三方,比如極光推送,融雲通訊;尤其對於極光推送來說,保持平臺的唯一性至關重要,為了避免讓極光id陷入混亂的局面,單點登入勢在必行。

2.實現方案

這裡筆者僅以自己的實現分享給大夥,如果有更好的方案,加群與筆者討論

1.在資料庫建立快取表login_cache,主要儲存userId以及對應的uuid(移動端生成的,每臺裝置的唯一碼)

2.在登入的時候將userId以及uuid插入快取表

//appId
存於快取表中 String cacheUserId = user.getId(); Integer cacheExits = loginCacheMapper.cacheUserIsExits(cacheUserId); if(cacheExits == 0){ map.put("userId",cacheUserId); map.put("uuid",uuid); loginCacheMapper.insertLoginCache(map); }else{ loginCacheMapper.updateCacheUUID(uuid,cacheUserId); }
3.在spring-mvc.xml中配置攔截器
<
mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/*/*" /> <bean id="springmvc-interceptor" class="cn.yivi.util.common.interceptor.Interceptor"> <property name="excludedUrls"> <list> <value>/mail/send</value>
</list> </property> </bean> </mvc:interceptor> </mvc:interceptors>
4.攔截器具體程式碼
public class Interceptor extends HandlerInterceptorAdapter {
   /** 日誌 */
private static final Logger logger;
/** 不攔截的介面 */
private List<String> excludedUrls;
/** 免登入可以訪問的介面列表 */
private static final List<String> noLoginRequiredUrls;
@Autowired
private LoginCacheMapper loginCacheMapper;
@Autowired
private UserMapper userMapper;
   static {
      logger = Logger.getLogger(cn.yivi.util.common.interceptor.Interceptor.class);
noLoginRequiredUrls = new ArrayList<String>();
noLoginRequiredUrls.add("/user/userRegister");
}

   public List<String> getExcludedUrls() {
      return excludedUrls;
}

   public void setExcludedUrls(List<String> excludedUrls) {
      this.excludedUrls = excludedUrls;
}

   @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
         throws Exception {

      /** 訪問金鑰 */
String token = request.getHeader(InterfaceParamsConfig.TOKEN);
/** 裝置ID */
String appId = request.getHeader(InterfaceParamsConfig.APP_ID);
/** 使用者主鍵 */
String userId  = request.getHeader(InterfaceParamsConfig.USER_ID);
/**UUID*/
String uuid = request.getHeader(InterfaceParamsConfig.UUID);
/** 請求地址 */
String requestUrl = request.getRequestURI();
/**
       * 介面訪問頻率限制
*/
for (String excludedUrl : excludedUrls) {
         if (requestUrl.contains(excludedUrl)) {
            return true;
}
      }


      for (String noLoginRequiredUrl : noLoginRequiredUrls) {
         if (requestUrl.endsWith(noLoginRequiredUrl)) {
            return true;
}
      }

      if (userId == null || userId.equals("")) {
         String msg = ResultJsonUtil.toErrorJSONString(ResponseErrorTipConfig.LOCK_BASIC_PARAMS);
writeResponse(response, ResponseStatusConfig.ERROR, HttpServletResponse.SC_UNAUTHORIZED, msg);
logger.error("requestUrl:" + requestUrl + System.getProperty("line.separator") + "params:" + msg);
         return false;
}

      String testUserId = userId;
String mobile = userMapper.findMobileById(userId); //使用者手機
System.out.println("====================:"+mobile);
System.out.println("====================:"+testUserId);
String cacheUUID = loginCacheMapper.getCacheUUID(userId);
      if(cacheUUID == null || cacheUUID.equals("")){
         cacheUUID = "";
}

      if (cacheUUID != null && !cacheUUID.equals("") && uuid != null && !uuid.equals("")) {
         if (uuid.equals(cacheUUID)) {
            return true;
}else{

            String msg = ResultJsonUtil.toErrorJSONString(ResponseErrorTipConfig.LOGIN_FAILURT);
writeResponse(response, ResponseStatusConfig.ERROR, HttpServletResponse.SC_UNAUTHORIZED, msg);
logger.error("requestUrl:uuid==" + uuid+",掉線了!");
logger.error("requestUrl:" + requestUrl + System.getProperty("line.separator") + "params:" + msg);
            return false;
}
      }

      return true;
}

   @Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {

   }

   @Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
         throws Exception {
      if (ex != null) {
         if (ex instanceof YiViException) {
            String msg = ResultJsonUtil.toExceptionJSONString(ResponseErrorTipConfig.LOCK_PARAMS);
writeResponse(response, ResponseStatusConfig.EXCEPTION, HttpServletResponse.SC_EXPECTATION_FAILED, msg);
} else {
            String msg = ResultJsonUtil.toExceptionJSONString(ResponseErrorTipConfig.SYSTEM_EXCEPTION);
writeResponse(response, ResponseStatusConfig.EXCEPTION, HttpServletResponse.SC_EXPECTATION_FAILED, msg);
}
      }
   }

   @Override
public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler)
         throws Exception {

   }

   private void writeResponse(HttpServletResponse response, ResponseStatusConfig headerStatus, int status, String msg)
         throws Exception {
      response.setContentType("application/json;charset=utf-8");
response.setCharacterEncoding("utf-8");
response.setIntHeader(InterfaceParamsConfig.STATUS, headerStatus.getValue());
response.setStatus(status);
response.getWriter().write(msg);
response.getWriter().flush();
response.getWriter().close();
}

}

這裡提醒下,token,userId,uuid必須申明為方法變數,切記不要使用類變數,否則會掉坑的!

5.基本思路

   移動端傳固定的uuid,然後登入的時候檢測一下該使用者是否存在於快取表,如果存在更改對應的uuid,如果不存在,則插入一條userId以及uuid對應的資料;然後在請求controller之前,進行攔截器攔截,取出對應請求的uuid及userId與快取中的uuid進行對比,相同就放行,不同則攔截丟擲錯誤碼讓移動端彈出登入介面

好了,今天就講到這裡,我是張星,歡迎加入博主技術交流群,群號:313145288