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

网友投稿 1337 2022-11-20

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

教你使用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 redisTemplate;

public JwtAuthenticationSuccessHandler(ObjectMapper jacksonObjectMapper, RedisTemplate 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, AuthendAMbZYTMeztication authentication) throws IOException {

ihttp://f (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 redisTemplate;

private final ObjectMapper jacksonObjectMapper;

public JwtSecurityContextRepository(RedisTemplate 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 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() {

});

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";

}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:什么是蠕虫,木马以及二者跟病毒是什么关系!
下一篇:一条双字长的取数指令(LDA)存于存储器的100和101单元,其中第一个字为操作码和寻址特征M,第二个字为形式地址。
相关文章

 发表评论

暂时没有评论,来抢沙发吧~