Java移動端後臺之單點登入
阿新 • • 發佈:2019-02-20
1.需求分析
單點登入在軟體開發中是一個老生常談的話題;以筆者公司的後臺專案為例,筆者公司的app是以航母戰鬥群的形式開發的,一個後臺必須供四五個app訪問,甚至更多;並且分app中還集成了第三方,比如極光推送,融雲通訊;尤其對於極光推送來說,保持平臺的唯一性至關重要,為了避免讓極光id陷入混亂的局面,單點登入勢在必行。
2.實現方案
這裡筆者僅以自己的實現分享給大夥,如果有更好的方案,加群與筆者討論
1.在資料庫建立快取表login_cache,主要儲存userId以及對應的uuid(移動端生成的,每臺裝置的唯一碼)
2.在登入的時候將userId以及uuid插入快取表
//將appId3.在spring-mvc.xml中配置攔截器存於快取表中 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); }
<4.攔截器具體程式碼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>
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