項目做到一半技術經理辭職了,留個爛攤子。web基本已做完,安卓的app要新寫,項目開發完做個總結,這東西已經是好久前做的了。
登錄接口很快就寫好了,登錄成功后用戶信息是緩存再session當中的
/** * 用戶登錄 * * @param user * @return */ @RequestMapping(value = "doLogin") @ResponseBody public Map doLogin(User user, HttpServletRequest request) { Map result = Maps.newHashMap(); try { LoginResult flag = FrontUserUtils.doLogin(user, request); if (flag == LoginResult.登錄成功) { Map<String, Object> map = new HashMap<>(); String[] propertys = new String[]{"name", "photo", "singleId"}; for (String property : propertys) { map.put(property, Reflections.invokeGetter(FrontUserUtils.getUserBySession(), property)); } result.put("flag", 1); result.put("user", map); return result; } else if (flag == LoginResult.用戶被鎖定) { result.put("flag", 0); result.put("msg", "用戶被鎖定,請聯系管理員!"); return result; } else if (flag == LoginResult.登錄失敗) { result.put("flag", 0); result.put("msg", "賬號或密碼錯誤!"); return result; } } catch (Exception e) { result.put("flag", 0); result.put("msg", "操作失敗!"); e.printStackTrace(); } return result; }
/
但是登錄成功后,再調用其他需要獲取登錄用戶信息的時候的時候發現在session中沒有,不對啊,明明自已用瀏覽器測試是OK的啊,我自己補不了解嘛,
技術經理也跑了,不知道怎么回事啊、咋整,我去問安卓,他也不懂啊,我也走吧。
然后debug測試吧,用瀏覽器不這樣啊,session是同一個吶。測試一下,在后台打印一下sessionid:
瀏覽器訪問接口:
模擬安卓訪問接口:
每次都生成了新的session,這么搞不行啊,每次請求都生成新session,我怎么緩存你登錄用戶的信息啊。
然后就想辦法解決啊 ,想來想去想了兩個方案
方案1:自定義身份識別方式(token),類似自己模擬session
再用戶登錄以后,在系統內生成一個唯一的身份標識,並將此標識返回給客戶端。身份標識保存15天,如果用戶,在其他終端登錄,或者15天超時在服務器沒有業務,將通知終端會話超時,重新登錄。
后台代碼就不貼了,使用的uuid生成的一個字符串,作為用戶的登錄標識,將用戶信息寫入緩存,類似key :user這種方式存儲。
用戶請求其他接口的時候,會被我的一個攔截器攔截,如果發現根據字符串查詢用戶查詢不到,就會將用戶剔除系統。
攔截器:
public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { User user = FrontUserUtils.getUser(); if (user == null || user.getId() == null || user.getId().equals("")) { returnJson(httpServletResponse); return false; } else { return true; } } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { e.notifyAll(); } private void returnJson(HttpServletResponse response) throws Exception { PrintWriter writer = null; response.setCharacterEncoding("UTF-8"); response.setContentType("text/html; charset=utf-8"); try { Map map = new HashMap(); map.put("flag", 0); map.put("msg", "登陸會話已經過期,請重新登陸!"); JSONObject jsonObject = JSONObject.fromObject(map); writer = response.getWriter(); writer.print(jsonObject); } catch (IOException e) { } finally { if (writer != null) writer.close(); } } }
這樣寫,犧牲掉了一部分系統性能,來實現安卓與服務器之間的長連接,因為基本攔截了所有的接口,每次都判斷是否登錄,或者登錄超時。
方案2:還是使用sessionId,其實兩種方式都差不多,不過我還是推薦這種,第一種的花同一時間用戶太多,session過多,可能導致其他的問題。
web瀏覽器保持會話,是因為有session的存在,不同的服務器在瀏覽器請求的時候會判斷是否為第一次請求,如果是第一次就回返回一個參數Set-Cookie,一般瀏覽器支持自己保存這個數據,這個數據也是瀏覽器與服務器保持會話的標識;
比如以百度為例:
再次請求相同的服務器,瀏覽器會從cookie中取出這些這值,加載到瀏覽器的請求中去
瀏覽器如果不禁用cookie的話,瀏覽器就會將服務器返回的數據保存到cookie當中去,session與cookie不清楚的,去查資料吧。
安卓的訪問方式
盡管安卓可以使用許多網絡框架來進行訪問,但是我不是很了解吶,加上公司的安卓開發也不甚了解,
我覺得他是使用httpclient類似的方式來進行訪問的,我就謝了一個代碼模擬了下安卓的請求,來獲取下服務器返回的數據,看能不能獲取到 Set-Cookie;
public class MainActivity { public static final String login_url="http://192.168.0.112:8080/login/doLogin?loginName=18866601116&password=123456zxc"; /** * 安卓登錄測試 * @param args * @throws Exception */ public static void main(String[] args) throws Exception { URL url=new URL(login_url); HttpURLConnection con=(HttpURLConnection) url.openConnection(); con.setRequestMethod("GET"); String cookieString=con.getHeaderField("Set-Cookie"); System.out.print(cookieString); } }
//控制台輸出

哎可以的。就讓安卓再第一次請求服務器的時候將這個sessionid保存下來吧,緩存到手機,
然后請求后台的時候,將保存的cookie放在httpPost的請求當中,這樣訪問服務器時,服務器就會認為是同一個用戶了。也就是說保持了這個會話。
其實兩種方式的原理都差不多,如果有更好的方案,也可以跟我說,學習嘛。