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中引入:
1.   
2.   com.auth0 
3.   java-jwt 
4.   2.2.0 
5.  
  1. 拦截器配置:
1.   
2.    
3.    
4.    
5.  

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

  1. 编写jwt的加密或者解密工具类:
1.  public class JWT { 
2.   private static final String SECRET = "HONGHUJWT1234567890QWERTYUIOPASDFGHJKLZXCVBNM"; 

4.   private static final String EXP = "exp"; 

6.   private static final String PAYLOAD = "payload"; 

8.   //加密 
9.   public static  String sign(T object, long maxAge) { 
10.   try { 
11.   final JWTSigner signer = new JWTSigner(SECRET); 
12.   final Map claims = new HashMap(); 
13.   ObjectMapper mapper = new ObjectMapper(); 
14.   String jsonString = mapper.writeValueAsString(object); 
15.   claims.put(PAYLOAD, jsonString); 
16.   claims.put(EXP, System.currentTimeMillis() + maxAge); 
17.   return signer.sign(claims); 
18.   } catch(Exception e) { 
19.   return null; 
20.   } 
21.   } 

23.   //解密 
24.   public static T unsign(String jwt, Class classT) { 
25.   final JWTVerifier verifier = new JWTVerifier(SECRET); 
26.   try { 
27.   final Map claims= verifier.verify(jwt); 
28.   if (claims.containsKey(EXP) && claims.containsKey(PAYLOAD)) { 
29.   String json = (String)claims.get(PAYLOAD); 
30.   ObjectMapper objectMapper = new ObjectMapper(); 
31.   return objectMapper.readValue(json, classT); 

33.   } 
34.   return null; 
35.   } catch (Exception e) { 
36.   return null; 
37.   } 
38.   } 
39.  }

这个加密工具类是我从网上找的,如果各位要修改,可以按照自己业务修改即可。

  1. 创建Login.java对象,用来进行jwt的加密或者解密:
1.  public class Login implements Serializable{ 
2.   /** 
3.   * 
4.   */ 
5.   private static final long serialVersionUID = 1899232511233819216L; 

7.   /** 
8.   * 用户id 
9.   */ 
10.   private String uid; 

12.   /** 
13.   * 登录用户名 
14.   */ 
15.   private String loginName; 

17.   /** 
18.   * 登录密码 
19.   */ 
20.   private String password; 

22.   public Login(){ 
23.   super(); 
24.   } 

26.   public Login(String uid, String loginName, String password){ 
27.   this.uid = uid; 
28.   this.loginName = loginName; 
29.   this.password = password; 
30.   } 

32.   public String getUid() { 
33.   return uid; 
34.   } 
35.   public void setUid(String uid) { 
36.   this.uid = uid; 
37.   } 
38.   public String getLoginName() { 
39.   return loginName; 
40.   } 
41.   public void setLoginName(String loginName) { 
42.   this.loginName = loginName; 
43.   } 
44.   public String getPassword() { 
45.   return password; 
46.   } 
47.   public void setPassword(String password) { 
48.   this.password = password; 
49.   } 

52.  }
  1. 定义RedisLogin对象,用来通过uid往redis进行user对象存储:
1.  public class RedisLogin implements Serializable{ 
2.   /** 
3.   * 
4.   */ 
5.   private static final long serialVersionUID = 8116817810829835862L; 

7.   /** 
8.   * 用户id 
9.   */ 
10.   private String uid; 

12.   /** 
13.   * jwt生成的token信息 
14.   */ 
15.   private String token; 

17.   /** 
18.   * 登录或刷新应用的时间 
19.   */ 
20.   private long refTime; 

22.   public RedisLogin(){ 

24.   } 

26.   public RedisLogin(String uid, String token, long refTime){ 
27.   this.uid = uid; 
28.   this.token = token; 
29.   this.refTime = refTime; 
30.   } 

32.   public String getUid() { 
33.   return uid; 
34.   } 
35.   public void setUid(String uid) { 
36.   this.uid = uid; 
37.   } 
38.   public String getToken() { 
39.   return token; 
40.   } 
41.   public void setToken(String token) { 
42.   this.token = token; 
43.   } 
44.   public long getRefTime() { 
45.   return refTime; 
46.   } 
47.   public void setRefTime(long refTime) { 
48.   this.refTime = refTime; 
49.   } 

53.  }
  1. 编写LoginInterceptor.java拦截器
1.  public class LoginInterceptor implements HandlerInterceptor{ 

3.   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 
4.   throws Exception { 
5.   PrintWriter writer = null; 
6.   HandlerMethod method = null; 
7.   try { 
8.   method = (HandlerMethod) handler; 
9.   } catch (Exception e) { 
10.   writer = response.getWriter(); 
11.   ResponseVO responseVO = ResponseCode.buildEnumResponseVO(ResponseCode.REQUEST_URL_NOT_SERVICE, false); 
12.   responseMessage(response, writer, responseVO); 
13.   return false; 
14.   } 
15.   IsLogin isLogin = method.getMethodAnnotation(IsLogin.class); 
16.   if(null == isLogin){ 
17.   return true; 
18.   } 

21.   response.setCharacterEncoding("utf-8"); 
22.   String token = request.getHeader("token"); 
23.   String uid = request.getHeader("uid"); 
24.   //token不存在 
25.   if(StringUtils.isEmpty(token)) { 
26.   writer = response.getWriter(); 
27.   ResponseVO responseVO = LoginResponseCode.buildEnumResponseVO(LoginResponseCode.LOGIN_TOKEN_NOT_NULL, false); 
28.   responseMessage(response, writer, responseVO); 
29.   return false; 
30.   } 
31.   if(StringUtils.isEmpty(uid)){ 
32.   writer = response.getWriter(); 
33.   ResponseVO responseVO = LoginResponseCode.buildEnumResponseVO(LoginResponseCode.USERID_NOT_NULL, false); 
34.   responseMessage(response, writer, responseVO); 
35.   return false; 
36.   } 

38.   Login login = JWT.unsign(token, Login.class); 
39.   //解密token后的loginId与用户传来的loginId判断是否一致 
40.   if(null == login || !StringUtils.equals(login.getUid(), uid)){ 
41.   writer = response.getWriter(); 
42.   ResponseVO responseVO = LoginResponseCode.buildEnumResponseVO(LoginResponseCode.USERID_NOT_UNAUTHORIZED, false); 
43.   responseMessage(response, writer, responseVO); 
44.   return false; 
45.   } 

47.   //验证登录时间 
48.   RedisLogin redisLogin = (RedisLogin)JedisUtils.getObject(uid); 
49.   if(null == redisLogin){ 
50.   writer = response.getWriter(); 
51.   ResponseVO responseVO = LoginResponseCode.buildEnumResponseVO(LoginResponseCode.RESPONSE_CODE_UNLOGIN_ERROR, false); 
52.   responseMessage(response, writer, responseVO); 
53.   return false; 
54.   } 

56.   if(!StringUtils.equals(token, redisLogin.getToken())){ 
57.   writer = response.getWriter(); 
58.   ResponseVO responseVO = LoginResponseCode.buildEnumResponseVO(LoginResponseCode.USERID_NOT_UNAUTHORIZED, false); 
59.   responseMessage(response, writer, responseVO); 
60.   return false; 
61.   } 
62.   //系统时间>有效期(说明已经超过有效期) 
63.   if (System.currentTimeMillis() > redisLogin.getRefTime()) { 
64.   writer = response.getWriter(); 
65.   ResponseVO responseVO = LoginResponseCode.buildEnumResponseVO(LoginResponseCode.LOGIN_TIME_EXP, false); 
66.   responseMessage(response, writer, responseVO); 
67.   return false; 
68.   } 

70.   //重新刷新有效期 
71.   redisLogin = new RedisLogin(uid, token, System.currentTimeMillis() + 60L* 1000L* 30L); 
72.   JedisUtils.setObject(uid , redisLogin, 360000000); 
73.   return true; 
74.   } 

76.   public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, 
77.   ModelAndView modelAndView) throws Exception { 

79.   } 

81.   public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 
82.   throws Exception { 

84.   } 

86.   private void responseMessage(HttpServletResponse response, PrintWriter out, ResponseVO responseVO) { 
87.   response.setContentType("application/json; charset=utf-8"); 
88.   JSONObject result = new JSONObject(); 
89.   result.put("result", responseVO); 
90.   out.print(result); 
91.   out.flush(); 
92.   out.close(); 
93.   } 

95.  }
  1. 定义异常的LoginResponseCode
1.  public enum LoginResponseCode { 
2.   USERID_NOT_NULL(3001,"用户id不能为空."), 
3.   LOGIN_TOKEN_NOT_NULL(3002,"登录token不能为空."), 
4.   USERID_NOT_UNAUTHORIZED(3003, "用户token或ID验证不通过"), 
5.   RESPONSE_CODE_UNLOGIN_ERROR(421, "未登录异常"), 
6.   LOGIN_TIME_EXP(3004, "登录时间超长,请重新登录"); 

8.   // 成员变量 
9.   private int code; //状态码 
10.   private String message; //返回消息 

12.   // 构造方法 
13.   private LoginResponseCode(int code,String message) { 
14.   this.code = code; 
15.   this.message = message; 
16.   } 
17.   public int getCode() { 
18.   return code; 
19.   } 
20.   public void setCode(int code) { 
21.   this.code = code; 
22.   } 
23.   public String getMessage() { 
24.   return message; 
25.   } 
26.   public void setMessage(String message) { 
27.   this.message = message; 
28.   } 

30.   public static ResponseVO buildEnumResponseVO(LoginResponseCode responseCode, Object data) { 
31.   return new ResponseVO(responseCode.getCode(),responseCode.getMessage(),data); 
32.   } 

34.   public static Map buildReturnMap(LoginResponseCode responseCode, Object data) { 
35.   Map map = new HashMap(); 
36.   map.put("code", responseCode.getCode()); 
37.   map.put("message", responseCode.getMessage()); 
38.   map.put("data", data); 
39.   return map; 
40.   } 
41.  }
  1. 编写统一sso单点登录接口:
1.  @RequestMapping(value = "/login", method = RequestMethod.POST) 
2.   public Map login(@RequestBody JSONObject json){ 
3.   String loginName = json.optString("loginName"); 
4.   String password = json.optString("password"); 
5.   //校验用户名不能为空 
6.   if(StringUtils.isEmpty(loginName)){ 
7.   return MemberResponseCode.buildReturnMap(MemberResponseCode.RESPONSE_CODE_USER_NAME_IS_NOT_EMPTY, null); 
8.   } 
9.   //校验用户密码不能为空 
10.   if(StringUtils.isEmpty(password)){ 
11.   return MemberResponseCode.buildReturnMap(MemberResponseCode.RESPONSE_CODE_PWD_CAN_NOT_BE_EMPTY, null); 
12.   } 
13.   //根据用户名查询数据库用户信息 
14.   User user = systemService.getBaseUserByLoginName(loginName); 
15.   //用户名或密码不正确 
16.   if(null == user){ 
17.   return MemberResponseCode.buildReturnMap(MemberResponseCode.RESPONSE_CODE_USER_VALIDATE_NO_SUCCESS, false); 
18.   } 
19.   boolean isValidate = systemService.validatePassword(password, user.getPassword()); 
20.   if(!isValidate){ 
21.   return MemberResponseCode.buildReturnMap(MemberResponseCode.RESPONSE_CODE_USER_VALIDATE_NO_SUCCESS, false); 
22.   } 
23.   if(isValidate){ 
24.   //HttpSession session =request.getSession(false); 
25.   Login login = new Login(user.getId(), user.getLoginName(), user.getPassword()); 
26.   //给用户jwt加密生成token 
27.   String token = JWT.sign(login, 60L* 1000L* 30L); 
28.   Map result =new HashMap(); 
29.   result.put("loginToken", token); 
30.   result.put("userId", user.getId()); 
31.   result.put("user", user); 

33.   //保存用户信息到session 
34.   //session.setAttribute(user.getId() + "@@" + token, user); 
35.   //重建用户信息 
36.   this.rebuildLoginUser(user.getId(), token); 
37.   return ResponseCode.buildReturnMap(ResponseCode.RESPONSE_CODE_LOGIN_SUCCESS, result); 
38.   } 

40.   return ResponseCode.buildReturnMap(ResponseCode.USER_LOGIN_PWD_ERROR, false); 
41.   }
  1. 测试sso单点登录:

spring+springmvc+Interceptor+jwt+redis实现sso单点登录_第1张图片
 返回结果集:

1.  { 
2.   "message": "用户登录成功", 
3.   "data": { 
4.   "loginToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDkzODA1OTU0NTksInBheWxvYWQiOiJ7XCJ1aWRcIjpcIjExXCIsXCJsb2dpbk5hbWVcIjpcImFkbWluXCIsXCJwYXNzd29yZFwiOlwiZjU0NGQxM2QyY2EwNDU5ZGQ0ZTU1NzVjNmZkYWIzMzM0MzE1MWFlZjgwYmE5ZTNiN2U1ZjM2MzJcIn0ifQ.56L60WtxHXSu9vNs6XsWy5zbmc3kP_IWG1YpReK50DM", 
5.   "userId": "11", 
6.   "user": { 
7.   "QQ":"2147775633", 
8.   "id": "11", 
9.   "isNewRecord": false, 
10.   "remarks": "", 
11.   "createDate": "2017-08-08 08:08:08", 
12.   "updateDate": "2017-10-29 11:23:50", 
13.   "loginName": "admin", 
14.   "no": "00012", 
15.   "name": "admin", 
16.   "email": "[email protected]", 
17.   "phone": "400000000", 
18.   "mobile": "13888888888", 
19.   "userType": "", 
20.   "loginIp": "0:0:0:0:0:0:0:1", 
21.   "loginDate": "2017-10-30 10:48:06", 
22.   "loginFlag": "1", 
23.   "photo": "", 
24.   "idCard": "420888888888888888", 
25.   "oldLoginIp": "0:0:0:0:0:0:0:1", 
26.   "oldLoginDate": "2017-10-30 10:48:06", 
27.   "roleNames": "", 
28.   "admin": false 
29.   } 
30.   }, 
31.   "code": 200 
32.  }

 到此完毕!!

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

你可能感兴趣的:(spring,java,小程序)