ITPub博客

首页 > 应用开发 > Java > spring+springmvc+Interceptor+jwt+redis实现sso单点登录

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

原创 Java 作者:fumi106 时间:2020-11-09 17:31:33 0 删除 编辑

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

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

 

Java代码   收藏代码
  1. <dependency>  
  2.             <groupId>com.auth0</groupId>  
  3.             <artifactId>java-jwt</artifactId>  
  4.             <version> 2.2 . 0 </version>  
  5. </dependency>  

 

 

2. 拦截器配置:

 

Java代码   收藏代码
  1. <mvc:interceptor>  
  2.             <mvc:mapping path= "${adminPath}/**"  />  
  3.             <mvc:exclude-mapping path= "${adminPath}/rest/login" />  
  4.             <bean  class = "com.ml.honghu.interceptor.LoginInterceptor"  />  
  5. </mvc:interceptor>  

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

 

 

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

 

Java代码   收藏代码
  1. public   class  JWT {  
  2.      private   static   final  String SECRET =  "HONGHUJWT1234567890QWERTYUIOPASDFGHJKLZXCVBNM" ;  
  3.   
  4.      private   static   final  String EXP =  "exp" ;  
  5.   
  6.      private   static   final  String PAYLOAD =  "payload" ;  
  7.   
  8.      //加密   
  9.      public   static  <T> String sign(T object,  long  maxAge) {  
  10.          try  {  
  11.              final  JWTSigner signer =  new  JWTSigner(SECRET);  
  12.              final  Map<String, Object> claims =  new  HashMap<String, Object>();  
  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.     }  
  22.   
  23.      //解密   
  24.      public   static <T> T unsign(String jwt, Class<T> classT) {  
  25.          final  JWTVerifier verifier =  new  JWTVerifier(SECRET);  
  26.          try  {  
  27.              final  Map<String,Object> 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);  
  32.   
  33.             }  
  34.              return   null ;  
  35.         }  catch  (Exception e) {  
  36.              return   null ;  
  37.         }  
  38.     }  
  39. }  

 

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

 

4. 创建Login.java对象,用来进行jwt的加密或者解密:

 

Java代码   收藏代码
  1. public   class  Login  implements  Serializable{  
  2.      /** 
  3.      *  
  4.      */   
  5.      private   static   final   long  serialVersionUID = 1899232511233819216L;  
  6.   
  7.      /** 
  8.      * 用户id 
  9.      */   
  10.      private  String uid;  
  11.       
  12.      /** 
  13.      * 登录用户名 
  14.      */   
  15.      private  String loginName;  
  16.       
  17.      /** 
  18.      * 登录密码 
  19.      */   
  20.      private  String password;  
  21.       
  22.      public  Login(){  
  23.          super ();  
  24.     }  
  25.       
  26.      public  Login(String uid, String loginName, String password){  
  27.          this .uid = uid;  
  28.          this .loginName = loginName;  
  29.          this .password = password;  
  30.     }  
  31.       
  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.     }  
  50.       
  51.       
  52. }  

 

 

5. 定义RedisLogin对象,用来通过uid往redis进行user对象存储:

 

Java代码   收藏代码
  1. public   class  RedisLogin  implements  Serializable{  
  2.      /** 
  3.      *  
  4.      */   
  5.      private   static   final   long  serialVersionUID = 8116817810829835862L;  
  6.   
  7.      /** 
  8.      * 用户id 
  9.      */   
  10.      private  String uid;  
  11.       
  12.      /** 
  13.      * jwt生成的token信息 
  14.      */   
  15.      private  String token;  
  16.       
  17.      /** 
  18.      * 登录或刷新应用的时间 
  19.      */   
  20.      private   long  refTime;  
  21.       
  22.      public  RedisLogin(){  
  23.           
  24.     }  
  25.       
  26.      public  RedisLogin(String uid, String token,  long  refTime){  
  27.          this .uid = uid;  
  28.          this .token = token;  
  29.          this .refTime = refTime;  
  30.     }  
  31.       
  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.     }  
  50.       
  51.       
  52.   
  53. }  

 

 

6. 编写LoginInterceptor.java拦截器

 

Java代码   收藏代码
  1. public   class  LoginInterceptor  implements  HandlerInterceptor{  
  2.   
  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.         }  
  19.           
  20.           
  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.         }  
  37.               
  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.         }  
  46.           
  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.         }  
  55.           
  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.         }  
  69.           
  70.          //重新刷新有效期   
  71.         redisLogin =  new  RedisLogin(uid, token, System.currentTimeMillis() + 60L* 1000L* 30L);  
  72.         JedisUtils.setObject(uid , redisLogin,  360000000 );  
  73.          return   true ;  
  74.     }  
  75.   
  76.      public   void  postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,  
  77.             ModelAndView modelAndView)  throws  Exception {  
  78.           
  79.     }  
  80.   
  81.      public   void  afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)  
  82.              throws  Exception {  
  83.           
  84.     }  
  85.       
  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.     }  
  94.   
  95. }  

 

 

7. 定义异常的LoginResponseCode

 

Java代码   收藏代码
  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 ,  "登录时间超长,请重新登录" );  
  7.       
  8.      // 成员变量     
  9.      private   int  code;  //状态码     
  10.      private  String message;  //返回消息   
  11.   
  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.     }    
  29.   
  30.      public   static  ResponseVO buildEnumResponseVO(LoginResponseCode responseCode, Object data) {  
  31.          return   new  ResponseVO(responseCode.getCode(),responseCode.getMessage(),data);  
  32.     }  
  33.       
  34.      public   static  Map<String, Object> buildReturnMap(LoginResponseCode responseCode, Object data) {  
  35.         Map<String, Object> map =  new  HashMap<String, Object>();  
  36.         map.put( "code" , responseCode.getCode());  
  37.         map.put( "message" , responseCode.getMessage());  
  38.         map.put( "data" , data);  
  39.          return  map;  
  40.     }  
  41. }  

 

 

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

 

Java代码   收藏代码
  1. @RequestMapping (value =  "/login" , method = RequestMethod.POST)  
  2.      public  Map<String, Object> 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<String,Object> result = new  HashMap<String,Object>();    
  29.             result.put( "loginToken" , token);  
  30.             result.put( "userId" , user.getId());  
  31.             result.put( "user" , user);  
  32.               
  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.         }  
  39.           
  40.          return  ResponseCode.buildReturnMap(ResponseCode.USER_LOGIN_PWD_ERROR,  false );  
  41.     }  

 

 

9. 测试sso单点登录:



 返回结果集:

Java代码   收藏代码
  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" :  "2147775633@qq.com" ,  
  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 Cloud大型企业分布式微服务云构建的B2B2C电子商务平台(企业架构源码可以加求球:叁五三陆二肆柒二伍玖)


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69984376/viewspace-2733120/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2020-09-22

  • 博文量
    51
  • 访问量
    25934