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

jwt介绍:

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

jwt认证流程介绍:

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

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

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

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

5. 服务器验证该jwt; 

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

一.思路:

原来的实现用户登录态是:
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";
    }

到此这篇关于springSecurity+jwt中实现互踢功能的文章就介绍到这了,更多相关springSecurity+jwt互踢内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • springboot+jwt+springSecurity微信小程序授权登录问题

    场景重现:1.微信小程序向后台发送请求 --而后台web采用的springSecuriry没有token生成,就会拦截请求,,所以小编记录下这个问题 微信小程序授权登录问题 思路 参考网上一大堆资料 核心关键字: 自定义授权+鉴权 (说的通俗就是解决办法就是改造springSecurity的过滤器) 参考文章 https://www.jb51.net/article/204704.htm 总的来说的 通过自定义的WxAppletAuthenticationFilter替换默认的UsernameP

  • 解析SpringSecurity+JWT认证流程实现

    纸上得来终觉浅,觉知此事要躬行. 楔子 本文适合:对Spring Security有一点了解或者跑过简单demo但是对整体运行流程不明白的同学,对SpringSecurity有兴趣的也可以当作你们的入门教程,示例代码中也有很多注释. 本文代码:码云地址  GitHub地址 大家在做系统的时候,一般做的第一个模块就是认证与授权模块,因为这是一个系统的入口,也是一个系统最重要最基础的一环,在认证与授权服务设计搭建好了之后,剩下的模块才得以安全访问. 市面上一般做认证授权的框架就是shiro和Spri

  • SpringSecurity整合springBoot、redis实现登录互踢功能

    背景 基于我的文章--<SpringSecurity整合springBoot.redis token动态url权限校验>.要实现的功能是要实现一个用户不可以同时在两台设备上登录,有两种思路: (1)后来的登录自动踢掉前面的登录. (2)如果用户已经登录,则不允许后来者登录. 需要特别说明的是,项目的基础是已经是redis维护的session. 配置redisHttpSession 设置spring session由redis 管理. 2.1去掉yml中的http session 配置,yml和

  • SpringSecurity Jwt Token 自动刷新的实现

    功能需求 最近项目中有这么一个功能,用户登录系统后,需要给 用户 颁发一个 token ,后续访问系统的请求都需要带上这个 token ,如果请求没有带上这个 token 或者 token 过期了,那么禁止访问系统.如果用户一直访问系统,那么还需要自动延长 token 的过期时间. 功能分析 1.token 的生成 使用现在比较流行的 jwt 来生成. 2.token 的自动延长 要实现 token 的自动延长,系统给用户 颁发 一个 token 无法实现,那么通过变通一个,给用户生成 2个 t

  • SpringSecurity+JWT实现前后端分离的使用详解

    创建一个配置类 SecurityConfig 继承 WebSecurityConfigurerAdapter package top.ryzeyang.demo.common.config; import org.springframework.context.annotation.Bean; import org.springframework.security.access.hierarchicalroles.RoleHierarchy; import org.springframework

  • SpringSecurity整合jwt权限认证的全流程讲解

    JWT 本文代码截取自实际项目. jwt(Json Web Token),一个token,令牌. 简单流程: 用户登录成功后,后端返回一个token,也就是颁发给用户一个凭证.之后每一次访问,前端都需要携带这个token,后端通过token来解析出当前访问对象. 优点 1.一定程度上解放了后端,后端不需要再记录当前用户是谁,不需要再维护一个session,节省了开销. 2.session依赖于cookie,某些场合cookie是用不了的,比如用户浏览器cookie被禁用.移动端无法存储cooki

  • SpringSecurity构建基于JWT的登录认证实现

    最近项目的登录验证部分,采用了 JWT 验证的方式.并且既然采用了 Spring Boot 框架,验证和权限管理这部分,就自然用了 Spring Security.这里记录一下具体实现. 在项目采用 JWT 方案前,有必要先了解它的特性和适用场景,毕竟软件工程里,没有银弹.只有合适的场景,没有万精油的方案. 一言以蔽之,JWT 可以携带非敏感信息,并具有不可篡改性.可以通过验证是否被篡改,以及读取信息内容,完成网络认证的三个问题:"你是谁"."你有哪些权限".&qu

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

    jwt介绍:         JWT是一种用于双方之间传递安全信息的简洁的.URL安全的表述性声明规范.JWT作为一个开放的标准( RFC 7519 ),定义了一种简洁的,自包含的方法用于通信双方之间以Json对象的形式安全的传递信息. jwt认证流程介绍: 1. 用户使用账号和面发出post请求:  2. 服务器使用私钥创建一个jwt:  3. 服务器返回这个jwt给浏览器:  4. 浏览器将该jwt串在请求头中像服务器发送请求:  5. 服务器验证该jwt:  6. 返回响应的资源给浏览器.

  • Spring Security OAuth2 实现登录互踢的示例代码

    本文主要介绍了Spring Security OAuth2 实现登录互踢的示例代码,分享给大家,具体如下: 背景说明 一个账号只能一处登录,类似的业务需求在现有后管类系统是非常常见的. 但在原有的 spring security oauth2 令牌方法流程(所谓的登录)无法满足类似的需求. 我们先来看 TokenEndpoint 的方法流程 客户端 带参访问 /oauth/token 接口,最后去调用 TokenGranter TokenGranter 根据不同的授权类型,获取用户认证信息 并去

  • Springboot+SpringSecurity+JWT实现用户登录和权限认证示例

    如今,互联网项目对于安全的要求越来越严格,这就是对后端开发提出了更多的要求,目前比较成熟的几种大家比较熟悉的模式,像RBAC 基于角色权限的验证,shiro框架专门用于处理权限方面的,另一个比较流行的后端框架是Spring-Security,该框架提供了一整套比较成熟,也很完整的机制用于处理各类场景下的可以基于权限,资源路径,以及授权方面的解决方案,部分模块支持定制化,而且在和oauth2.0进行了很好的无缝连接,在移动互联网的授权认证方面有很强的优势,具体的使用大家可以结合自己的业务场景进行选

  • Java SpringSecurity+JWT实现登录认证

    目录 整合步骤 实现原理 目录结构 做了哪些变化 前言: 学习过我的mall项目的应该知道,mall-admin模块是使用SpringSecurity+JWT来实现登录认证的,而mall-portal模块是使用的SpringSecurity基于Session的默认机制来实现登陆认证的.很多小伙伴都找不到mall-portal的登录接口,最近我把这两个模块的登录认证给统一了,都使用SpringSecurity+JWT的形式实现.主要是通过把登录认证的通用逻辑抽取到了mall-security模块来

  • SpringBoot+SpringSecurity+jwt实现验证

    目录 环境 目录结构信息 记录一下使用springSecurity实现jwt的授权方法,这方法可以实现权限的基本认证.当然这个案例还有许多的问题,不过还是先记录一下.其他功能以后在补充. 建议工程创建流程 创建 JwtTokenUtils 创建 jwtAccessDeniedHandler 和 JwtAuthenticationEntryPoint 创建 UserDetailsServiceImpl 创建 JwtAuthenticationFilter 配置 Security信息 启动类的信息

  • SpringBoot+SpringSecurity+JWT实现系统认证与授权示例

    目录 1. Spring Security简介 2. JWT简介 3. Spring Boot整合Spring Security 4. 配置Spring Security使用JWT认证 5. 实现登录接口 6. 测试 7. 源码 1. Spring Security简介 Spring Security是Spring的一个核心项目,它是一个功能强大且高度可定制的认证和访问控制框架.它提供了认证和授权功能以及抵御常见的攻击,它已经成为保护基于spring的应用程序的事实标准. Spring Boot

随机推荐