spring+springmvc+Interceptor+jwt+redis实现sso单点登录

在分布式环境中,如何支持PC、APP(ios、android)等多端的会话共享,这也是所有公司都需要的解决方案,用传统的session方式来解决,我想已经out了,我们是否可以找一个通用的方案,比如用传统cas来实现多系统之间的sso单点登录或使用oauth的第三方登录方案? 今天给大家简单讲解一下使用spring拦截器Interceptor机制、jwt认证方式、redis分布式缓存实现sso单点登录,闲话少说,直接把步骤记录下来分享给大家:

1. 引入jwt的相关jar包,在项目pom.xml中引入:

              com.auth0              java-jwt              2.2.0     

2. 拦截器配置:

                                              

我这里简单配置了要拦截的url和过滤的url(这个根据自己项目来定)

3. 编写jwt的加密或者解密工具类:

public class JWT {      private static final String SECRET = "HONGHUJWT1234567890QWERTYUIOPASDFGHJKLZXCVBNM";        private static final String EXP = "exp";        private static final String PAYLOAD = "payload";        //加密      public static  String sign(T object, long maxAge) {          try {              final JWTSigner signer = new JWTSigner(SECRET);              final Map claims = new HashMap();              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 unsign(String jwt, Class classT) {          final JWTVerifier verifier = new JWTVerifier(SECRET);          try {              final Map 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的加密或者解密:

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对象存储:

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拦截器

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. 定义异常的LoginResponseCode

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 buildReturnMap(LoginResponseCode responseCode, Object data) {          Map map = new HashMap();          map.put("code", responseCode.getCode());          map.put("message", responseCode.getMessage());          map.put("data", data);          return map;      }  }  

8. 编写统一sso单点登录接口:

@RequestMapping(value = "/login", method = RequestMethod.POST)      public Map 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 result =new HashMap();                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单点登录:

返回结果集:

{    "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": "[email protected]",        "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  }  

 到此完毕!!


用java实施的电子商务平台太少了,使用spring cloud技术构建的b2b2c电子商务平台更少,大型企业分布式互联网电子商务平台,推出PC+微信+APP+云服务的云商平台系统,其中包括B2B、B2C、C2C、O2O、新零售、直播电商等子平台。

Spring Cloud大型企业分布式微服务云构建的B2B2C电子商务平台(企业架构源码可以加求球:叁五三陆二肆柒二伍玖)

你可能感兴趣的:(spring+springmvc+Interceptor+jwt+redis实现sso单点登录)