教你使用springSecurity+jwt实现互踢功能

作者:txSong123 时间:2023-05-24 19:45:10 

jwt介绍:

        JWT是一种用于双方之间传递安全信息的简洁的、URL安全的表述性声明规范。JWT作为一个开放的标准( RFC 7519 ),定义了一种简洁的,自包含的方法用于通信双方之间以Json对象的形式安全的传递信息。

jwt认证流程介绍:

1. 用户使用账号和面发出post请求; 

2. 服务器使用私钥创建一个jwt; 

3. 服务器返回这个jwt给浏览器; 

4. 浏览器将该jwt串在请求头中像服务器发送请求; 

5. 服务器验证该jwt; 

6. 返回响应的资源给浏览器。

教你使用springSecurity+jwt实现互踢功能

一.思路:

原来的实现用户登录态是:
1.后台登陆成功后生成一个令牌(uuid)----JwtAuthenticationSuccessHandler
2.后台把它包装成jwt数据,然后返回给前端—JwtAuthenticationSuccessHandler
3.后台把它加入redis缓存中,并设置失效时间----JwtAuthenticationSuccessHandler
4.前端调用接口时,带入jwt
5.后台写个 * 或者过滤器,在前端调用接口的时候,从request的header中获取jwt,在缓存中搜索,如果存在则处于登录态,并重置失效时间(这样用户在有效时间内就处于登录态)—JwtSecurityContextRepository
6.解释下:springSecurity是个过滤器琏,是由一个一个的过滤器组成的

现在的互踢:
1.后台在登陆成功后,用用户id组成一个key,查询redis缓存中的value
2.和新的jwt比较,如果不一样则把查到的jwt当做key从redis中删掉,就是上面第三步存储的
3.把用户id组成一个key,把上面jwt当做value传入缓存中
4.在上面的第5步,也重置下我们这里存储的值

*上面 指的是原来的实现用户登录态


package com.lc.gansu.security.component.jwt;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.lc.gansu.framework.core.ConstantOfReturnCode;
import com.lc.gansu.framework.core.RedisKey;
import com.lc.gansu.framework.core.ReturnObject;
import com.lc.gansu.security.component.SecurityConstants;
import com.lc.gansu.security.domain.User;
import com.lc.gansu.security.utility.JWTHS256;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Slf4j
public class JwtAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

//private final RequestCache requestCache = new HttpSessionRequestCache();
   private final ObjectMapper jacksonObjectMapper;
   private final RedisTemplate<String, Object> redisTemplate;

public JwtAuthenticationSuccessHandler(ObjectMapper jacksonObjectMapper, RedisTemplate<String, Object> redisTemplate) {
       this.jacksonObjectMapper = jacksonObjectMapper;
       this.redisTemplate = redisTemplate;
   }

@Override
   public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
       log.info("JwtAuthenticationSuccessHandler=success");
       clearAuthenticationAttributes(request);
       handle(response, authentication);
   }

protected final void clearAuthenticationAttributes(HttpServletRequest request) {
       HttpSession session = request.getSession(false);
       if (session == null) return;
       session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
   }

protected void handle(HttpServletResponse response, Authentication authentication) throws IOException {
       if (response.isCommitted()) {
           log.debug("Response has already been committed.");
           return;
       }
       User sysUser = (User) authentication.getPrincipal();
       sysUser.setClazz(authentication.getClass());
       //AuthenticationAdapter authenticationAdapter=AuthenticationAdapter.authentication2AuthenticationAdapter(authentication);
       String authOfjson = jacksonObjectMapper.writeValueAsString(sysUser);
       String subject = UUID.randomUUID().toString();
       String authOfjwt = JWTHS256.buildJWT(subject, authOfjson);
       response.addHeader("jwt", authOfjwt);
       //跨域时允许header携带jwt
       response.addHeader("Access-Control-Expose-Headers" ,"jwt");
       redisTemplate.boundValueOps(SecurityConstants.getJwtKey(subject)).set("w", 60, TimeUnit.MINUTES);
       //---------互踢start-------------
       // 在缓存中传入key="R_V_USERIDLOGINKEY_" + userId + "_LOGIN",value=SecurityConstants.getJwtKey(subject),有新登录时如果用户一样则把缓存里之前的jwt删除,这个:(redisTemplate.boundValueOps(SecurityConstants.getJwtKey(subject)).set("w", 60, TimeUnit.MINUTES);)
       log.info("设置jwt,并在缓存中传入:{}",SecurityConstants.getJwtKey(subject));
       String uuid = (String) redisTemplate.opsForValue().get(RedisKey.getUserIdKey(sysUser.getId()));
       log.info("查询原有jwt:{}",uuid);
       if(!SecurityConstants.getJwtKey(subject).equals(uuid)&& Objects.nonNull(uuid)){
           assert uuid != null;
           redisTemplate.delete(uuid);
           log.info("删除原有jwt:{}",uuid);
       }
       redisTemplate.opsForValue().set(RedisKey.getUserIdKey(sysUser.getId()), SecurityConstants.getJwtKey(subject),60, TimeUnit.MINUTES);
       log.info("在缓存里塞入新jwt:{}",SecurityConstants.getJwtKey(subject));
       //---------互踢end----------------------
       response.setContentType("application/json;charset=utf-8");
       PrintWriter out = response.getWriter();

User returnSysUser = new User();
       returnSysUser
               .setName(sysUser.getName())
               .setCurrentOrg(sysUser.getCurrentOrg())
               .setOrgIdMapRoleList(sysUser.getOrgIdMapRoleList())
               .setCurrentMenuList(sysUser.getCurrentMenuList())
               .setOrgList(sysUser.getOrgList());

out.write(jacksonObjectMapper.writeValueAsString(new ReturnObject<>(this.getClass().getName(), ConstantOfReturnCode.GLOBAL_RESULT_SUCESS, "登录成功", returnSysUser)));
       out.flush();
       out.close();
   }
}


package com.lc.gansu.security.component.jwt;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lc.gansu.framework.core.RedisKey;
import com.lc.gansu.security.component.SecurityConstants;
import com.lc.gansu.security.domain.User;
import com.lc.gansu.security.utility.JWTHS256;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.context.HttpRequestResponseHolder;
import org.springframework.security.web.context.SecurityContextRepository;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Slf4j
public class JwtSecurityContextRepository implements SecurityContextRepository {
   protected final Log logger = LogFactory.getLog(this.getClass());

private final RedisTemplate<String, Object> redisTemplate;
   private final ObjectMapper jacksonObjectMapper;

public JwtSecurityContextRepository(RedisTemplate<String, Object> redisTemplate, ObjectMapper jacksonObjectMapper) {
       this.redisTemplate = redisTemplate;
       this.jacksonObjectMapper = jacksonObjectMapper;
   }

@Override
   public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
       HttpServletRequest request = requestResponseHolder.getRequest();
       return readSecurityContextFromJWT(request);
   }

@Override
   public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
   }

@Override
   public boolean containsContext(HttpServletRequest request) {
       return false;
   }

private SecurityContext readSecurityContextFromJWT(HttpServletRequest request) {
       SecurityContext context = generateNewContext();
       String authenticationOfjwt = request.getHeader("jwt");
       if (StringUtils.isNotBlank(authenticationOfjwt)) {
           try {
               Map<String, Object> map = JWTHS256.vaildToken(authenticationOfjwt);
               if (Objects.nonNull(map) && map.size() == 2) {
                   String subject = (String) map.get("subject");
                   Boolean isExp = redisTemplate.hasKey(SecurityConstants.getJwtKey(subject));
                   if (Objects.nonNull(isExp) && isExp) {//redis key 未过期
                       redisTemplate.expire(SecurityConstants.getJwtKey(subject), 60, TimeUnit.MINUTES);//延期
                       String obj = (String) map.get("claim");
                       //AuthenticationAdapter authenticationAdapter=jacksonObjectMapper.readValue(obj, new TypeReference<>(){});
                       //Authentication authentication=AuthenticationAdapter.authenticationAdapter2Authentication(authenticationAdapter);
                       //Authentication authentication=jacksonObjectMapper.readValue(obj, new TypeReference<>(){});
                       //Authentication authentication=jacksonObjectMapper.readValue(obj,Authentication.class);
                       User sysUser = jacksonObjectMapper.readValue(obj, new TypeReference<User>() {
                       });
                       Authentication authentication = new UsernamePasswordAuthenticationToken(sysUser, null, sysUser.getAuthorities());
                       context.setAuthentication(authentication);
                       //-----互踢start-------
                       if(Objects.nonNull(RedisKey.getUserIdKey(sysUser.getId()))) redisTemplate.expire(RedisKey.getUserIdKey(sysUser.getId()), 60, TimeUnit.MINUTES);
                       //-----互踢end---------
                       //if(obj instanceof Authentication){
                       //context.setAuthentication((Authentication)obj);
                       //}else log.error("jwt包含authentication的数据非法");
                   } else log.error("jwt数据过期");
               } else log.error("jwt数据非法");
           } catch (Exception e) {
               e.printStackTrace();
               logger.error(e.getLocalizedMessage());
           }
       } else {
           if (logger.isDebugEnabled()) {
               logger.debug("No JWT was available from the HttpServletRequestHeader!");
           }
       }
       return context;
   }

protected SecurityContext generateNewContext() {
       return SecurityContextHolder.createEmptyContext();
   }
}


package com.lc.gansu.framework.core;

/**
* TODO
*
* @author songtianxiong
* @version 1.0
* @date 2021/11/16 19:15
*/
public class RedisKey {
   //线上投放计划反馈结果催办
   public static String getOlpmIdAndUserIdRedisKey(Long OlpmId, Long userId) {
       return "R_V_OLPMURGE_" + OlpmId + "_" + userId;
   }

//催办
   public static String getOdpIdAndUserIdRedisKey(Long OdpId, Long userId) {
       return "R_V_ODPMURGE_" + OdpId + "_" + userId;
   }

//催办
   public static String getJwtAndUrl(String jwt, String url) {
       return "R_V_REPEAT_" + jwt + "_" + url;
   }

//用户登录互踢
   public static String getUserIdKey(Long userId) {
       return "R_V_USERIDLOGINKEY_" + userId + "_LOGIN";
   }
}

---------------------------关键词:互踢

关键代码:


       //---------互踢start-------------
       // 在缓存中传入key="R_V_USERIDLOGINKEY_" + userId + "_LOGIN",value=SecurityConstants.getJwtKey(subject),有新登录时如果用户一样则把缓存里之前的jwt删除,这个:(redisTemplate.boundValueOps(SecurityConstants.getJwtKey(subject)).set("w", 60, TimeUnit.MINUTES);)
       log.info("设置jwt,并在缓存中传入:{}",SecurityConstants.getJwtKey(subject));
       String uuid = (String) redisTemplate.opsForValue().get(RedisKey.getUserIdKey(sysUser.getId()));
       log.info("查询原有jwt:{}",uuid);
       if(!SecurityConstants.getJwtKey(subject).equals(uuid)&& Objects.nonNull(uuid)){
           assert uuid != null;
           redisTemplate.delete(uuid);
           log.info("删除原有jwt:{}",uuid);
       }
       redisTemplate.opsForValue().set(RedisKey.getUserIdKey(sysUser.getId()), SecurityConstants.getJwtKey(subject),60, TimeUnit.MINUTES);
       log.info("在缓存里塞入新jwt:{}",SecurityConstants.getJwtKey(subject));
       //---------互踢end----------------------

                       //-----互踢start-------
                       if(Objects.nonNull(RedisKey.getUserIdKey(sysUser.getId()))) redisTemplate.expire(RedisKey.getUserIdKey(sysUser.getId()), 60, TimeUnit.MINUTES);
                       //-----互踢end---------

   //用户登录互踢
   public static String getUserIdKey(Long userId) {
       return "R_V_USERIDLOGINKEY_" + userId + "_LOGIN";
   }

来源:https://blog.csdn.net/jockha/article/details/121509230

标签:springSecurity,jwt,互踢
0
投稿

猜你喜欢

  • java实现简单计算器功能

    2021-06-17 11:21:23
  • Android控件之ListView用法实例详解

    2023-09-11 00:39:26
  • Android编程绘图操作之弧形绘制方法示例

    2021-10-06 00:33:45
  • Android RadioButton单选框的使用方法

    2021-10-02 14:37:20
  • Java IO流 文件传输基础

    2023-08-24 13:27:11
  • 浅析JAVA中的内存结构、重载、this与继承

    2023-09-24 19:15:07
  • java LeetCode刷题稍有难度的贪心构造算法

    2022-01-06 20:56:21
  • 基于mvc5+ef6+Bootstrap框架实现身份验证和权限管理

    2023-07-08 09:25:22
  • Android系统优化Ninja加快编译

    2022-06-30 19:43:14
  • SpringBoot整合Web开发之文件上传与@ControllerAdvice

    2021-09-29 04:43:55
  • Java WebService开源框架CXF详解

    2023-12-20 13:02:35
  • Android实现自动提取短信验证码功能

    2022-11-19 23:58:49
  • 三种Android单击事件onclick的实现方法

    2022-05-21 16:54:03
  • Android中的Intent Filter匹配规则简介

    2021-07-29 16:02:30
  • java WebSocket实现聊天消息推送功能

    2023-09-12 00:41:55
  • Java 序列化和反序列化实例详解

    2023-11-23 04:43:44
  • JDK14之jpackage打包命令的使用

    2022-09-29 19:57:19
  • Jmeter配置代理实现录制过程图解

    2022-01-15 20:25:32
  • Android实现中轴旋转特效 Android制作别样的图片浏览器

    2023-07-03 00:37:16
  • JAVA Map架构和API介绍

    2023-01-25 14:01:38
  • asp之家 软件编程 m.aspxhome.com