提交 d35c3f23 编写于 作者: H haoxr

feat:添加强制会话失效功能

上级 a5757bed
package com.youlai.mall.pms.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.youlai.common.web.pojo.vo.CascaderVO;
import com.youlai.common.web.vo.CascaderVO;
import com.youlai.mall.pms.pojo.domain.PmsCategory;
import com.youlai.mall.pms.pojo.vo.CategoryVO;
......
......@@ -3,7 +3,7 @@ package com.youlai.mall.pms.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.common.web.pojo.vo.CascaderVO;
import com.youlai.common.web.vo.CascaderVO;
import com.youlai.mall.pms.pojo.domain.PmsCategory;
import com.youlai.mall.pms.mapper.PmsCategoryMapper;
import com.youlai.mall.pms.service.IPmsCategoryService;
......
......@@ -10,8 +10,6 @@ import lombok.Data;
@Data
public class LoginRecord extends BaseDocument {
private String description;
private String clientIP;
private long elapsedTime;
......@@ -26,4 +24,9 @@ public class LoginRecord extends BaseDocument {
private String region;
/**
* 会话状态 0-离线 1-在线
*/
private Integer status;
}
package com.youlai.admin.common.util;
import cn.hutool.json.JSONUtil;
import com.nimbusds.jose.JWSObject;
import com.youlai.common.constant.AuthConstants;
import com.youlai.common.domain.JWTPayload;
import lombok.SneakyThrows;
import org.apache.logging.log4j.util.Strings;
/**
* @author hxr
* @date 2021-03-10
*/
public class JWTUtils {
/**
* 获取JWT的载体
* @param token
* @return
*/
@SneakyThrows
public static JWTPayload getJWTPayload(String token) {
token = token.replace(AuthConstants.AUTHORIZATION_PREFIX, Strings.EMPTY);
JWSObject jwsObject = JWSObject.parse(token);
JWTPayload payload = JSONUtil.toBean(jwsObject.getPayload().toString(), JWTPayload.class);
return payload;
}
/**
* 判断token是否过期
* @param token
* @return
*/
public static boolean isExpired(String token) {
JWTPayload payload = getJWTPayload(token);
// 计算是否过期
long currentTimeSeconds = System.currentTimeMillis() / 1000;
Long exp = payload.getExp();
if (exp < currentTimeSeconds) { // token已过期,无需加入黑名单
return true;
}
return false;
}
}
......@@ -3,6 +3,7 @@ package com.youlai.admin.controller;
import cn.hutool.core.util.StrUtil;
import com.youlai.admin.common.constant.ESConstants;
import com.youlai.admin.pojo.domain.LoginRecord;
import com.youlai.admin.service.ITokenService;
import com.youlai.common.base.BaseDocument;
import com.youlai.common.elasticsearch.service.ElasticSearchService;
import com.youlai.common.result.Result;
......@@ -18,6 +19,7 @@ import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
......@@ -33,6 +35,8 @@ public class LoginRecordController {
ElasticSearchService elasticSearchService;
ITokenService tokenService;
@ApiOperation(value = "列表分页", httpMethod = "GET")
@ApiImplicitParams({
@ApiImplicitParam(name = "page", value = "页码", defaultValue = "1", paramType = "query", dataType = "Long"),
......@@ -73,6 +77,13 @@ public class LoginRecordController {
// 分页查询
List<LoginRecord> list = elasticSearchService.search(queryBuilder, sortBuilder, page, limit, LoginRecord.class, ESConstants.LOGIN_INDEX_PATTERN);
// 遍历获取会话状态
list.forEach(item->{
int tokenStatus = tokenService.getTokenStatus(item.getToken());
item.setStatus(tokenStatus);
});
return Result.success(list, count);
}
......@@ -84,4 +95,5 @@ public class LoginRecordController {
documents.forEach(document -> elasticSearchService.deleteById(document.getId(), document.getIndex()));
return Result.success();
}
}
package com.youlai.admin.controller;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.nimbusds.jose.JWSObject;
import com.youlai.common.constant.AuthConstants;
import com.youlai.admin.service.ITokenService;
import com.youlai.common.result.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
......@@ -11,11 +8,11 @@ import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
/**
* @author hxr
......@@ -29,29 +26,15 @@ import java.util.concurrent.TimeUnit;
@AllArgsConstructor
public class TokenController {
RedisTemplate redisTemplate;
ITokenService tokenService;
@ApiOperation(value = "强制下线", httpMethod = "POST")
@ApiImplicitParam(name = "token", value = "访问令牌", required = true, paramType = "query", dataType = "String")
@PostMapping("/{token}/_invalid")
@PostMapping("/{token}/_invalidate")
@SneakyThrows
public Result invalidToken(@PathVariable String token) {
token = token.replace(AuthConstants.AUTHORIZATION_PREFIX, Strings.EMPTY);
JWSObject jwsObject = JWSObject.parse(token);
String payload = jwsObject.getPayload().toString();
JSONObject jsonObject = JSONUtil.parseObj(payload);
long currentTimeSeconds = System.currentTimeMillis() / 1000;
String jti = jsonObject.getStr(AuthConstants.JWT_JTI); // JWT唯一标识
long exp = jsonObject.getLong(AuthConstants.JWT_EXP); // JWT过期时间戳
if (exp < currentTimeSeconds) { // token已过期,无需加入黑名单
return Result.success();
}
redisTemplate.opsForValue().set(AuthConstants.TOKEN_BLACKLIST_PREFIX + jti, null, (exp - currentTimeSeconds), TimeUnit.SECONDS);
return Result.success();
public Result invalidateToken(@PathVariable String token) {
boolean status = tokenService.invalidateToken(token);
return Result.judge(status);
}
}
package com.youlai.admin.service;
import java.text.ParseException;
public interface ITokenService {
/**
* 使令牌token失效
*
* @param token
* @return
*/
boolean invalidateToken(String token) throws ParseException;
/**
* 获取token状态
* @param token
* @return 1-有效,0-失效(过期或被加入黑名单)
*/
int getTokenStatus(String token);
}
package com.youlai.admin.service.impl;
import com.youlai.admin.common.util.JWTUtils;
import com.youlai.admin.service.ITokenService;
import com.youlai.common.constant.AuthConstants;
import com.youlai.common.domain.JWTPayload;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
/**
* @author haoxr
* @date 2021-03-10
*/
@Service
@AllArgsConstructor
public class TokenServiceImpl implements ITokenService {
RedisTemplate redisTemplate;
@Override
@SneakyThrows
public boolean invalidateToken(String token) {
JWTPayload payload = JWTUtils.getJWTPayload(token);
// 计算是否过期
long currentTimeSeconds = System.currentTimeMillis() / 1000;
Long exp = payload.getExp();
if (exp < currentTimeSeconds) { // token已过期,无需加入黑名单
return true;
}
// 添加至黑名单使其失效
redisTemplate.opsForValue().set(AuthConstants.TOKEN_BLACKLIST_PREFIX + payload.getJti(), null, (exp - currentTimeSeconds), TimeUnit.SECONDS);
return true;
}
@Override
public int getTokenStatus(String token) {
JWTPayload payload = JWTUtils.getJWTPayload(token);
// 计算是否过期
long currentTimeSeconds = System.currentTimeMillis() / 1000;
Long exp = payload.getExp();
if (exp < currentTimeSeconds) { // token已过期 返回失效
return 0;
}
// 判断是否存在黑名单
String jti = payload.getJti();
Boolean isExists = redisTemplate.hasKey(AuthConstants.TOKEN_BLACKLIST_PREFIX + jti);
if (isExists == true) { // 被添加到黑名单 返回失效
return 0;
}
return 1;
}
}
package com.youlai.common.domain;
import lombok.Data;
/**
* @author hxr
* @date 2021-03-10
*/
@Data
public class JWTPayload {
private String jti;
private Long exp;
}
package com.youlai.common.web.pojo.vo;
package com.youlai.common.web.vo;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册