在分佈式環境中,如何支持PC、APP(ios、android)等多端的會話共享,這也是全部公司都須要的解決方案,用傳統的session方式來解決,我想已經out了,咱們是否能夠找一個通用的方案,好比用傳統cas來實現多系統之間的sso單點登陸或使用oauth的第三方登陸方案? 今天給你們簡單講解一下使用spring攔截器Interceptor機制、jwt認證方式、redis分佈式緩存實現sso單點登陸,閒話少說,直接把步驟記錄下來分享給你們:java
1. 引入jwt的相關jar包,在項目pom.xml中引入:android
Java代碼
- <dependency>
- <groupId>com.auth0</groupId>
- <artifactId>java-jwt</artifactId>
- <version>2.2.0</version>
- </dependency>
2. 攔截器配置:ios
Java代碼
- <mvc:interceptor>
- <mvc:mapping path="${adminPath}/**" />
- <mvc:exclude-mapping path="${adminPath}/rest/login"/>
- <bean class="com.ml.honghu.interceptor.LoginInterceptor" />
- </mvc:interceptor>
我這裏簡單配置了要攔截的url和過濾的url(這個根據本身項目來定)redis
3. 編寫jwt的加密或者解密工具類:spring
Java代碼
- public class JWT {
- private static final String SECRET = "HONGHUJWT1234567890QWERTYUIOPASDFGHJKLZXCVBNM";
-
- private static final String EXP = "exp";
-
- private static final String PAYLOAD = "payload";
-
- //加密
- public static <T> String sign(T object, long maxAge) {
- try {
- final JWTSigner signer = new JWTSigner(SECRET);
- final Map<String, Object> claims = new HashMap<String, Object>();
- ObjectMapper mapper = new ObjectMapper();
- String jsonString = mapper.writeValueAsString(object);
- claims.put(PAYLOAD, jsonString);
- claims.put(EXP, System.currentTimeMillis() + maxAge);
- return signer.sign(claims);
- } catch(Exception e) {
- return null;
- }
- }
-
- //解密
- public static<T> T unsign(String jwt, Class<T> classT) {
- final JWTVerifier verifier = new JWTVerifier(SECRET);
- try {
- final Map<String,Object> claims= verifier.verify(jwt);
- if (claims.containsKey(EXP) && claims.containsKey(PAYLOAD)) {
- String json = (String)claims.get(PAYLOAD);
- ObjectMapper objectMapper = new ObjectMapper();
- return objectMapper.readValue(json, classT);
-
- }
- return null;
- } catch (Exception e) {
- return null;
- }
- }
- }
這個加密工具類是我從網上找的,若是各位要修改,能夠按照本身業務修改便可。數據庫
4. 建立Login.java對象,用來進行jwt的加密或者解密:json
Java代碼
- public class Login implements Serializable{
- /**
- *
- */
- private static final long serialVersionUID = 1899232511233819216L;
-
- /**
- * 用戶id
- */
- private String uid;
-
- /**
- * 登陸用戶名
- */
- private String loginName;
-
- /**
- * 登陸密碼
- */
- private String password;
-
- public Login(){
- super();
- }
-
- public Login(String uid, String loginName, String password){
- this.uid = uid;
- this.loginName = loginName;
- this.password = password;
- }
-
- public String getUid() {
- return uid;
- }
- public void setUid(String uid) {
- this.uid = uid;
- }
- public String getLoginName() {
- return loginName;
- }
- public void setLoginName(String loginName) {
- this.loginName = loginName;
- }
- public String getPassword() {
- return password;
- }
- public void setPassword(String password) {
- this.password = password;
- }
-
-
- }
5. 定義RedisLogin對象,用來經過uid往redis進行user對象存儲:緩存
Java代碼
- public class RedisLogin implements Serializable{
- /**
- *
- */
- private static final long serialVersionUID = 8116817810829835862L;
-
- /**
- * 用戶id
- */
- private String uid;
-
- /**
- * jwt生成的token信息
- */
- private String token;
-
- /**
- * 登陸或刷新應用的時間
- */
- private long refTime;
-
- public RedisLogin(){
-
- }
-
- public RedisLogin(String uid, String token, long refTime){
- this.uid = uid;
- this.token = token;
- this.refTime = refTime;
- }
-
- public String getUid() {
- return uid;
- }
- public void setUid(String uid) {
- this.uid = uid;
- }
- public String getToken() {
- return token;
- }
- public void setToken(String token) {
- this.token = token;
- }
- public long getRefTime() {
- return refTime;
- }
- public void setRefTime(long refTime) {
- this.refTime = refTime;
- }
-
-
-
- }
6. 編寫LoginInterceptor.java攔截器session
Java代碼
- public class LoginInterceptor implements HandlerInterceptor{
-
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
- throws Exception {
- PrintWriter writer = null;
- HandlerMethod method = null;
- try {
- method = (HandlerMethod) handler;
- } catch (Exception e) {
- writer = response.getWriter();
- ResponseVO responseVO = ResponseCode.buildEnumResponseVO(ResponseCode.REQUEST_URL_NOT_SERVICE, false);
- responseMessage(response, writer, responseVO);
- return false;
- }
- IsLogin isLogin = method.getMethodAnnotation(IsLogin.class);
- if(null == isLogin){
- return true;
- }
-
-
- response.setCharacterEncoding("utf-8");
- String token = request.getHeader("token");
- String uid = request.getHeader("uid");
- //token不存在
- if(StringUtils.isEmpty(token)) {
- writer = response.getWriter();
- ResponseVO responseVO = LoginResponseCode.buildEnumResponseVO(LoginResponseCode.LOGIN_TOKEN_NOT_NULL, false);
- responseMessage(response, writer, responseVO);
- return false;
- }
- if(StringUtils.isEmpty(uid)){
- writer = response.getWriter();
- ResponseVO responseVO = LoginResponseCode.buildEnumResponseVO(LoginResponseCode.USERID_NOT_NULL, false);
- responseMessage(response, writer, responseVO);
- return false;
- }
-
- Login login = JWT.unsign(token, Login.class);
- //解密token後的loginId與用戶傳來的loginId判斷是否一致
- if(null == login || !StringUtils.equals(login.getUid(), uid)){
- writer = response.getWriter();
- ResponseVO responseVO = LoginResponseCode.buildEnumResponseVO(LoginResponseCode.USERID_NOT_UNAUTHORIZED, false);
- responseMessage(response, writer, responseVO);
- return false;
- }
-
- //驗證登陸時間
- RedisLogin redisLogin = (RedisLogin)JedisUtils.getObject(uid);
- if(null == redisLogin){
- writer = response.getWriter();
- ResponseVO responseVO = LoginResponseCode.buildEnumResponseVO(LoginResponseCode.RESPONSE_CODE_UNLOGIN_ERROR, false);
- responseMessage(response, writer, responseVO);
- return false;
- }
-
- if(!StringUtils.equals(token, redisLogin.getToken())){
- writer = response.getWriter();
- ResponseVO responseVO = LoginResponseCode.buildEnumResponseVO(LoginResponseCode.USERID_NOT_UNAUTHORIZED, false);
- responseMessage(response, writer, responseVO);
- return false;
- }
- //系統時間>有效期(說明已經超過有效期)
- if (System.currentTimeMillis() > redisLogin.getRefTime()) {
- writer = response.getWriter();
- ResponseVO responseVO = LoginResponseCode.buildEnumResponseVO(LoginResponseCode.LOGIN_TIME_EXP, false);
- responseMessage(response, writer, responseVO);
- return false;
- }
-
- //從新刷新有效期
- redisLogin = new RedisLogin(uid, token, System.currentTimeMillis() + 60L* 1000L* 30L);
- JedisUtils.setObject(uid , redisLogin, 360000000);
- return true;
- }
-
- public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
- ModelAndView modelAndView) throws Exception {
-
- }
-
- public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
- throws Exception {
-
- }
-
- private void responseMessage(HttpServletResponse response, PrintWriter out, ResponseVO responseVO) {
- response.setContentType("application/json; charset=utf-8");
- JSONObject result = new JSONObject();
- result.put("result", responseVO);
- out.print(result);
- out.flush();
- out.close();
- }
-
- }
7. 定義異常的LoginResponseCodemvc
Java代碼
- public enum LoginResponseCode {
- USERID_NOT_NULL(3001,"用戶id不能爲空."),
- LOGIN_TOKEN_NOT_NULL(3002,"登陸token不能爲空."),
- USERID_NOT_UNAUTHORIZED(3003, "用戶token或ID驗證不經過"),
- RESPONSE_CODE_UNLOGIN_ERROR(421, "未登陸異常"),
- LOGIN_TIME_EXP(3004, "登陸時間超長,請從新登陸");
-
- // 成員變量
- private int code; //狀態碼
- private String message; //返回消息
-
- // 構造方法
- private LoginResponseCode(int code,String message) {
- this.code = code;
- this.message = message;
- }
- public int getCode() {
- return code;
- }
- public void setCode(int code) {
- this.code = code;
- }
- public String getMessage() {
- return message;
- }
- public void setMessage(String message) {
- this.message = message;
- }
-
- public static ResponseVO buildEnumResponseVO(LoginResponseCode responseCode, Object data) {
- return new ResponseVO(responseCode.getCode(),responseCode.getMessage(),data);
- }
-
- public static Map<String, Object> buildReturnMap(LoginResponseCode responseCode, Object data) {
- Map<String, Object> map = new HashMap<String, Object>();
- map.put("code", responseCode.getCode());
- map.put("message", responseCode.getMessage());
- map.put("data", data);
- return map;
- }
- }
8. 編寫統一sso單點登陸接口:
Java代碼
- @RequestMapping(value = "/login", method = RequestMethod.POST)
- public Map<String, Object> login(@RequestBody JSONObject json){
- String loginName = json.optString("loginName");
- String password = json.optString("password");
- //校驗用戶名不能爲空
- if(StringUtils.isEmpty(loginName)){
- return MemberResponseCode.buildReturnMap(MemberResponseCode.RESPONSE_CODE_USER_NAME_IS_NOT_EMPTY, null);
- }
- //校驗用戶密碼不能爲空
- if(StringUtils.isEmpty(password)){
- return MemberResponseCode.buildReturnMap(MemberResponseCode.RESPONSE_CODE_PWD_CAN_NOT_BE_EMPTY, null);
- }
- //根據用戶名查詢數據庫用戶信息
- User user = systemService.getBaseUserByLoginName(loginName);
- //用戶名或密碼不正確
- if(null == user){
- return MemberResponseCode.buildReturnMap(MemberResponseCode.RESPONSE_CODE_USER_VALIDATE_NO_SUCCESS, false);
- }
- boolean isValidate = systemService.validatePassword(password, user.getPassword());
- if(!isValidate){
- return MemberResponseCode.buildReturnMap(MemberResponseCode.RESPONSE_CODE_USER_VALIDATE_NO_SUCCESS, false);
- }
- if(isValidate){
- //HttpSession session =request.getSession(false);
- Login login = new Login(user.getId(), user.getLoginName(), user.getPassword());
- //給用戶jwt加密生成token
- String token = JWT.sign(login, 60L* 1000L* 30L);
- Map<String,Object> result =new HashMap<String,Object>();
- result.put("loginToken", token);
- result.put("userId", user.getId());
- result.put("user", user);
-
- //保存用戶信息到session
- //session.setAttribute(user.getId() + "@@" + token, user);
- //重建用戶信息
- this.rebuildLoginUser(user.getId(), token);
- return ResponseCode.buildReturnMap(ResponseCode.RESPONSE_CODE_LOGIN_SUCCESS, result);
- }
-
- return ResponseCode.buildReturnMap(ResponseCode.USER_LOGIN_PWD_ERROR, false);
- }
9. 測試sso單點登陸:
返回結果集:
Java代碼
- {
- "message": "用戶登陸成功",
- "data": {
- "loginToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDkzODA1OTU0NTksInBheWxvYWQiOiJ7XCJ1aWRcIjpcIjExXCIsXCJsb2dpbk5hbWVcIjpcImFkbWluXCIsXCJwYXNzd29yZFwiOlwiZjU0NGQxM2QyY2EwNDU5ZGQ0ZTU1NzVjNmZkYWIzMzM0MzE1MWFlZjgwYmE5ZTNiN2U1ZjM2MzJcIn0ifQ.56L60WtxHXSu9vNs6XsWy5zbmc3kP_IWG1YpReK50DM",
- "userId": "11",
- "user": {
- "QQ":"2147775633",
- "id": "11",
- "isNewRecord": false,
- "remarks": "",
- "createDate": "2017-08-08 08:08:08",
- "updateDate": "2017-10-29 11:23:50",
- "loginName": "admin",
- "no": "00012",
- "name": "admin",
- "email": "2147775633@qq.com",
- "phone": "400000000",
- "mobile": "13888888888",
- "userType": "",
- "loginIp": "0:0:0:0:0:0:0:1",
- "loginDate": "2017-10-30 10:48:06",
- "loginFlag": "1",
- "photo": "",
- "idCard": "420888888888888888",
- "oldLoginIp": "0:0:0:0:0:0:0:1",
- "oldLoginDate": "2017-10-30 10:48:06",
- "roleNames": "",
- "admin": false
- }
- },
- "code": 200
- }
到此完畢!!