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中引入:Java代码

<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>2.2.0</version></dependency>2. 拦截器配置: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(这个根据自己项目来定)3. 编写jwt的加密或者解密工具类: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的加密或者解密: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拦截器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. 定义异常的LoginResponseCodeJava代码

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加密生成tokenString 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}到此完毕!!(企业架构源码可以加求球:叁五三陆二肆柒二伍玖)用java实施的电子商务平台太少了,使用spring cloud技术构建的b2b2c电子商务平台更少,大型企业分布式互联网电子商务平台,推出PC+微信+APP+云服务的云商平台系统,其中包括B2B、B2C、C2C、O2O、新零售、直播电商等子平台。

(0)

相关推荐