这篇文章,我们将学习怎样初步集成sa-token来实现基本的登录认证和权限校验,并初步看下底层的源码实现。
sa-token是一个轻量级的登录鉴权框架,比之前的shiro和springsecurity都要轻量,一行代码就可以实现登录功能。具体文档可以看:sa-token官网。这里我们不再赘述。直接来看具体实现代码。
点击看项目源码
<dependency>
<groupId>cn.dev33groupId>
<artifactId>sa-token-spring-boot-starterartifactId>
<version>1.34.0version>
dependency>
<dependency>
<groupId>cn.dev33groupId>
<artifactId>sa-token-dao-redis-jacksonartifactId>
<version>1.34.0version>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
dependency>
其中,如果是springboot3.x版本的话,上面的sa-token-spring-boot-starter依赖要换成sa-token-spring-boot3-starter
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SaInterceptor(handler -> {
SaRouter.match("/**", StpUtil::checkLogin)
.notMatch(SaTokenConstant.excludePathPatterns);
// 这里可以给每个接口地址的路径鉴权,当然,也可以用注解在每个接口上鉴权
})).addPathPatterns(SaTokenConstant.allRouters)
.excludePathPatterns(SaTokenConstant.excludePathPatterns);
}
@Bean
@Primary
public SaTokenConfig getSaTokenConfigPrimary(){
SaTokenConfig config = new SaTokenConfig();
// token名称
config.setTokenName(SaTokenConstant.TOKEN_NAME);
// token有效期,单位 秒,默认是30天
config.setTimeout(30 * 24 * 60 * 60);
// token无操作存活时间(指定时间内无操作就视为token过期) 单位:秒
config.setActivityTimeout(SaTokenConstant.ACTIVITY_TIMEOUT);
// 是否允许同一账号并发登录,true是允许多个同一账号一起登录,false时同一账号新登录的会挤掉旧登录的
config.setIsConcurrent(false);
// 在多人登录同一账号时,是否共用一个token
config.setIsShare(true);
// token风格
config.setTokenStyle(TokenStyleEnum.SIMPLE_UUID.getTokenStyle());
// 是否输出操作日志
config.setIsLog(false);
/*
是否尝试从 cookie 里读取 Token,此值为 false 后,StpUtil.login(id) 登录时也不会再往前端注入Cookie,
false时,可以返回给前端token,然后让前端在每次请求时header里携带返回的token,
这样可以用于不能使用cookie的设备端,不在局限于web浏览器端
*/
config.setIsReadCookie(false);
return config;
}
}
/**
* 全局异常处理
*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 全局登录异常处理
* @param exception 登录抛出的异常
* @return 提示信息
*/
@ExceptionHandler(NotLoginException.class)
public BaseResult<Object> handlerNotLoginException(NotLoginException exception) {
// 打印日志
log.error(exception.getMessage(), exception);
// 判断登录异常的场景,定制化返回提示
String type = exception.getType();
switch (type){
case NotLoginException.INVALID_TOKEN:
case NotLoginException.TOKEN_TIMEOUT:
return BaseResult.fail(520, SaTokenConstant.TOKEN_OVERDUE);
case NotLoginException.BE_REPLACED:
case NotLoginException.KICK_OUT:
return BaseResult.fail(520, SaTokenConstant.LOGIN_REPLACE);
default:
return BaseResult.fail(520, SaTokenConstant.NOT_TOKEN);
}
}
/**
* 全局权限校验异常处理
* @param exception 权限校验异常
* @return
*/
@ExceptionHandler(NotPermissionException.class)
public BaseResult<String> handlerNotPermissionException(NotPermissionException exception){
// 打印日志
log.error(exception.getMessage(), exception);
return BaseResult.fail(520, SaTokenConstant.PERMISSION_ERROR);
}
}
这里我们模拟下真实的登录场景:
用户在登录页一般会看到三个输入框,分别是:账号、密码和验证码图片。那么我们首先要在登录页面获取到验证码图片,并在登录接口中传入验证码并进行校验,那么后端就必须要存下返回给前端的验证码,那么我们需要先返回前端一个临时token,然后前端拿着这个临时token来请求获取验证码的接口,后端拿到这个临时token后,生成验证码,并将这个临时token作为缓存的key,验证码内容作为value存入缓存中。等到前端调用真正的登录接口时,需要传入 账号、密码、验证码和临时token,在这个接口中,我们通过临时token,去缓存中拿到验证码,和前端传入的验证码进行比对,然后再校验账号和密码。
@RestController
@Slf4j
public class LoginController {
@Resource
private VerifyImgUtil verifyImgUtil;
@Resource
private LoginService loginService;
/**
* 在获取验证码接口和登录接口前,先调用,获取一个临时token,用来作为验证码的key
*
* @return
*/
@GetMapping("tempToken")
public BaseResult<String> getTempToken(@RequestParam("tokenKey") String tokenKey) {
log.info("开始获取临时token------>" + tokenKey);
String token = SaTempUtil.createToken(tokenKey, 5 * 60);
loginService.saveTempToken(token);
return BaseResult.success(token, null);
}
/**
* 获取图片验证码
*
* @param tempToken 获取到的临时token
* @param response
*/
@GetMapping("getVerifyImg")
public BaseResult<String> getVerifyImg(@RequestParam("tempToken") String tempToken, HttpServletResponse response) throws IOException {
log.info("开始获取图片验证码------>" + tempToken);
if (!loginService.checkTempToken(tempToken)) {
return BaseResult.fail(530, LoginConstant.TOKEN_EMPTY);
}
response.setContentType("image/jpeg");//设置相应类型,告诉浏览器输出的内容为图片
response.setHeader("Pragma", "No-cache");//设置响应头信息,告诉浏览器不要缓存此内容
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
OutputStream os = response.getOutputStream();
verifyImgUtil.lineCaptcha(os, tempToken);
try {
os.flush();
} finally {
os.close();
}
return null;
}
/**
* 模拟登录接口,真实场景中,可以先提供一个接口,返回一个临时token,
* 然后再提供一个获取验证码的接口,里面需要传入刚才返回的临时token,生成验证码后,以临时token作为key,验证码作为value放入缓存,
* 然后再来调用这个正式登录接口,里面需要传验证码和临时token以及用户的账号密码,先根据临时token从缓存中获取验证码来比对,
* 然后再比对账号密码,比对成功后,删除缓存中对应临时token的验证码数据,并删除临时token
*
* @param dto 登录数据
* @return
*/
@PostMapping("/login")
public BaseResult<String> login(@RequestBody LoginDto dto) {
log.info("开始登录------>" + JSON.toJSONString(dto));
BaseResult<String> result = loginService.checkLogin(dto);
if (!result.isState()) {
return result;
}
loginService.deleteTempToken(dto.getTempToken());// 校验成功后移除临时token
// 登录
StpUtil.login("10001");
// 将用户的数据放入上下文session中
StpUtil.getSession().set(LoginConstant.LOGIN_NAME, "admin");
StpUtil.getSession().set(LoginConstant.REAL_NAME, "管理员");
StpUtil.getSession().set(LoginConstant.USER_ID, "10001");
return BaseResult.success(StpUtil.getTokenValue(), "登录成功");
}
/**
* 注销登录接口
*/
@PostMapping("logout")
public void logout(){
StpUtil.logout();
}
}
public interface LoginService {
/**
* 登录校验
* @param dto
* @return
*/
BaseResult<String> checkLogin(LoginDto dto);
/**
* 存放临时token
* @param tempToken 临时token
*/
void saveTempToken(String tempToken);
/**
* 删除临时token
* @param tempToken 临时token
*/
void deleteTempToken(String tempToken);
/**
* 校验临时token是否有效
* @param tempToken 临时token
* @return
*/
boolean checkTempToken(String tempToken);
}
@Service
public class LoginServiceImpl implements LoginService {
@Resource
private RedisUtil redisUtil;
private static final String tempTokenPre = "tempToken";
@Override
public BaseResult<String> checkLogin(LoginDto dto) {
if (StringUtils.isBlank(dto.getTempToken())) {
return BaseResult.fail(530, LoginConstant.TOKEN_EMPTY);
}
if (StringUtils.isBlank(dto.getVerifyCode())) {
return BaseResult.fail(530, LoginConstant.VERIFY_CODE_EMPTY);
}
if (StringUtils.isBlank(dto.getLoginName())) {
return BaseResult.fail(530, LoginConstant.LOGIN_NAME_EMPTY);
}
if (StringUtils.isBlank(dto.getPassword())) {
return BaseResult.fail(530, LoginConstant.PASSWORD_EMPTY);
}
// 校验
Object verifyCode = redisUtil.get(dto.getTempToken());
if (verifyCode == null) {
return BaseResult.fail(530, LoginConstant.VERIFY_OVERDUE);
}
if (!dto.getVerifyCode().equalsIgnoreCase(verifyCode.toString())) {
return BaseResult.fail(530, LoginConstant.VERIFY_CODE_ERROR);
}
if (!"admin".equals(dto.getLoginName())) {
return BaseResult.fail(530, LoginConstant.LOGIN_NAME_ERROR);
}
if (!"123".equals(dto.getPassword())) {
return BaseResult.fail(530, LoginConstant.PASSWORD_ERROR);
}
// 校验成功后,移除redis
redisUtil.delete(dto.getTempToken());
return BaseResult.success(null, null);
}
@Override
public void saveTempToken(String tempToken) {
redisUtil.set(tempTokenPre + tempToken, "1", 300);
}
@Override
public void deleteTempToken(String tempToken) {
redisUtil.delete(tempTokenPre + tempToken);
}
@Override
public boolean checkTempToken(String tempToken) {
Object o = redisUtil.get(tempTokenPre + tempToken);
return o != null;
}
}
@Data
public class LoginDto implements Serializable {
/**
* 账号
*/
private String loginName;
/**
* 密码
*/
private String password;
/**
* 验证码
*/
private String verifyCode;
/**
* 临时token,用来校验验证码
*/
private String tempToken;
}
@Data
public class BaseResult<T> implements Serializable {
/**
* 返回数据
*/
private T data;
/**
* 提示信息
*/
private String message;
/**
* 状态码
*/
private int code;
/**
* 状态
*/
private boolean state = true;
public static <T> BaseResult<T> result(int code, String message, boolean state) {
BaseResult<T> result = new BaseResult<>();
result.setCode(code);
result.setMessage(message);
result.setState(state);
return result;
}
/**
* 返回信息
*
* @param code 状态码
* @param message 信息
* @param t 数据
* @param T
* @return ResultVo
*/
public static <T> BaseResult<T> result(int code, String message, T t, boolean state) {
BaseResult<T> r = new BaseResult<>();
r.setCode(code);
r.setMessage(message);
r.setData(t);
r.setState(state);
return r;
}
/**
* 返回成功
*
* @param data data
* @param T
* @return ResultVo
*/
public static <T> BaseResult<T> success(T data, String message) {
return result(HttpStatus.OK.value(), message, data, true);
}
/**
* 返回失败
*
* @param code code
* @param T
* @return ResultVo
*/
public static <T> BaseResult<T> fail(int code, String message) {
return result(code, message, null, false);
}
}
/**
* 要排除登录校验的接口地址枚举类
*/
@Getter
public enum ExcludePathEnum {
TEMP_TOKEN("/tempToken", "获取临时token的接口"),
VERIFY_IMG("/getVerifyImg", "获取验证码图片的接口"),
LOGIN("/login", "登录接口"),
LOGOUT("/logout", "注销登录接口");
private final String path;
private final String remark;
ExcludePathEnum(String path, String remark) {
this.path = path;
this.remark = remark;
}
}
public class LoginConstant {
public static final String TOKEN_EMPTY = "token不能为空!";
public static final String LOGIN_NAME_EMPTY = "请输入账号!";
public static final String PASSWORD_EMPTY = "请输入密码!";
public static final String LOGIN_NAME_ERROR = "账号不存在!";
public static final String PASSWORD_ERROR = "密码错误,请重新输入!";
public static final String VERIFY_CODE_EMPTY = "验证码不能为空!";
public static final String VERIFY_CODE_ERROR = "验证码错误,请重新输入!";
public static final String VERIFY_OVERDUE = "验证码已过期,请重新登录!";
public static final String LOGIN_NAME = "loginName";
public static final String REAL_NAME = "userName";
public static final String USER_ID = "userId";
}
public class SaTokenConstant {
/**
* token的字段名
*/
public static final String TOKEN_NAME = "token";
/**
* token无操作存活时间(指定时间内无操作就视为token过期) 单位:秒
*/
public static final long ACTIVITY_TIMEOUT = 24 * 60 * 60;
public static final List<String> allRouters = Collections.singletonList("/**");
public static final List<String> excludePathPatterns = Arrays.asList(
"/test/**",
"/file/upload/img/head/**",
"/swagger-resources/**",
"/webjars/**",
"/v2/**",
"/doc.html",
"**/swagger-ui.html",
"/swagger-ui.html/**",
"/img/head/**",
ExcludePathEnum.TEMP_TOKEN.getPath(),
ExcludePathEnum.VERIFY_IMG.getPath(),
ExcludePathEnum.LOGIN.getPath(),
ExcludePathEnum.LOGOUT.getPath()
);
public static final String NOT_TOKEN = "您未登录,请登录!";
public static final String TOKEN_OVERDUE = "登录已失效,请重新登录!";
public static final String LOGIN_REPLACE = "您的账号已在别处登录!";
public static final String PERMISSION_ERROR = "您没有权限!";
}
@Getter
public enum TokenStyleEnum {
/**
* 一般的uuid风格
*/
UUID("uuid"),
/**
* uuid风格,只不过去掉了中划线
*/
SIMPLE_UUID("simple-uuid"),
/**
* 随机32位字符串
*/
RANDOM_32("random-32"),
/**
* 随机64位字符串
*/
RANDOM_64("random-64"),
/**
* 随机128位字符串
*/
RANDOM_128("random-128"),
/**
* tik风格,gr_SwoIN0MC1ewxHX_vfCW3BothWDZMMtx__
*/
TIK("tik");
private final String tokenStyle;
TokenStyleEnum(String tokenStyle) {
this.tokenStyle = tokenStyle;
}
}
这里的验证码图片生成用的是hultool包的工具类,需要先导入hultool包
maven:
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>5.8.15version>
dependency>
封装的工具类:
@Component
public class VerifyImgUtil {
@Resource
private RedisUtil redisUtil;
/**
* 生成验证码的字符,去掉了0、1、I、O这样容易混淆的字符
*/
public static final String VERIFY_CODES = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
/**
* 生成线段干扰的验证码
*
* @param os 输出流
* @param tempToken 临时token,作为验证码写入缓存的key
*/
public void lineCaptcha(OutputStream os, String tempToken) {
//定义图形验证码的长和宽
LineCaptcha captcha = CaptchaUtil.createLineCaptcha(200, 100);
// 自定义纯数字的验证码(随机4位字符,可重复)
RandomGenerator randomGenerator = new RandomGenerator(VERIFY_CODES, 4);
captcha.setGenerator(randomGenerator);
putVerifyCode(tempToken, captcha.getCode());
captcha.write(os);
}
/**
* 生成圆圈干扰的验证码
*
* @param os 输出流
* @param tempToken 临时token,作为验证码写入缓存的key
*/
public void circleCaptcha(OutputStream os, String tempToken) {
//定义图形验证码的长、宽、验证码字符数、干扰元素个数
CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(200, 100, 4, 20);
// 自定义纯数字的验证码(随机4位字符,可重复)
RandomGenerator randomGenerator = new RandomGenerator(VERIFY_CODES, 4);
captcha.setGenerator(randomGenerator);
putVerifyCode(tempToken, captcha.getCode());
captcha.write(os);
}
/**
* 生成扭曲干扰的验证码
*
* @param os 输出流
* @param tempToken 临时token,作为验证码写入缓存的key
*/
public void shearCaptcha(OutputStream os, String tempToken) {
//定义图形验证码的长、宽、验证码字符数、干扰线宽度
ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(200, 100, 4, 4);
// 自定义纯数字的验证码(随机4位字符,可重复)
RandomGenerator randomGenerator = new RandomGenerator(VERIFY_CODES, 4);
captcha.setGenerator(randomGenerator);
putVerifyCode(tempToken, captcha.getCode());
captcha.write(os);
}
/**
* 将验证码放到缓存里
*
* @param tempToken 临时token,作为验证码写入缓存的key
* @param verifyCode 验证码
*/
private void putVerifyCode(String tempToken, String verifyCode) {
redisUtil.set(tempToken, verifyCode, 40);
}
}
@Component
public class RedisUtil {
@Resource
private RedisTemplate<String, Object> redisTemplate;
/**
* 获取符合要求的所有key
* @param patternKey 指定要求的key
* @return 获取符合要求的所有key集合
*/
public Set<String> getAllKey(String patternKey){
return redisTemplate.keys(patternKey);
}
/**
* 清空所有的redis数据库
*/
public void flushAll(){
redisTemplate.getConnectionFactory().getConnection().flushAll();
}
/**
* 清空当前数据库
*/
public void flushDB(){
redisTemplate.getConnectionFactory().getConnection().flushDb();
}
/**
* 指定缓存的有效期
* @param key 缓存key
* @param time 有效时间,已秒为单位
* @return
*/
public Boolean expire(String key, long time){
if (time > 0){
return redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
}
/**
* 获取指定key的过期时间,返回0表示永久有效
* @param key 缓存key
* @return 过期时间
*/
public Long getExpire(String key){
return redisTemplate.getExpire(key);
}
/**
* 判断指定的缓存key是否存在
* @param key 缓存key
* @return
*/
public Boolean hasKey(String key){
return redisTemplate.hasKey(key);
}
/**
* 删除指定的key,可以传入一个String数组,批量删除
* @param key 缓存key数组
*/
public void delete(String... key){
if (key == null || key.length < 1){
return;
}
if (key.length == 1){
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(Arrays.asList(key));
}
}
/**
* String类型的添加缓存数据
* @param key 缓存key
* @param value 缓存value
*/
public void set(String key, Object value){
redisTemplate.opsForValue().set(key, value);
}
/**
* String类型的添加缓存数据,并设置过期时间,单位为秒
* @param key 缓存key
* @param value 缓存value
* @param time 过期时间
*/
public void set(String key, Object value, long time){
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
}
/**
* 获取指定key的缓存数据
* @param key 缓存key
* @return
*/
public Object get(String key){
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 指定key的值递增
* @param key 缓存key
* @param delta 递增因子,即每次递增加几
* @return
*/
public Long incr(String key, long delta){
if (delta < 0){
throw new RuntimeException("递增因子要大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 指定key的值递减
* @param key 缓存key
* @param delta 递减因子,即每次递减减几
* @return
*/
public Long decr(String key, long delta){
if (delta < 0){
throw new RuntimeException("递减因子要大于0");
}
return redisTemplate.opsForValue().decrement(key, delta);
}
/**
* Hash类型
* 获取指定key的指定键值对的值
* @param key 缓存key
* @param hashKey hash类型的键值对的key
* @return
*/
public Object hget(String key, String hashKey){
return redisTemplate.opsForHash().get(key, hashKey);
}
/**
* Hash类型
* 获取指定key的所有键值对的值
* @param key 缓存key
* @return
*/
public Map<Object, Object> hmget(String key){
return redisTemplate.opsForHash().entries(key);
}
/**
* Hash类型
* 添加指定缓存key的多个键值对
* @param key 缓存key
* @param map 对应的多个键值对
*/
public void hmset(String key, Map<Object, Object> map){
redisTemplate.opsForHash().putAll(key, map);
}
/**
* Hash类型
* 添加指定缓存key的多个键值对,并设置过期时间,以秒为单位
* @param key 缓存key
* @param map 对应的多个键值对
* @param time 过期时间
*/
public void hmset(String key, Map<Object, Object> map, long time){
redisTemplate.opsForHash().putAll(key, map);
expire(key, time);
}
/**
* Hash类型
* 添加指定缓存key的一个键值对
* @param key 换存key
* @param hashKey 对应的键值对的key
* @param value 对应的键值对的value
*/
public void hSet(String key, String hashKey, Object value){
redisTemplate.opsForHash().put(key, hashKey, value);
}
/**
* Hash类型
* 添加指定缓存key的一个键值对,并设置过期时间,以秒为单位
* @param key 缓存key
* @param hashKey 对应的键值对的key
* @param value 对应的键值对的value
* @param time 过期时间
*/
public void hSet(String key, String hashKey, Object value, long time){
hSet(key, hashKey, value);
expire(key, time);
}
/**
* Hash类型
* 删除指定key的指定键值对
* @param key 缓存key
* @param hashKey 要删除的键值对的key
*/
public void hDelete(String key, Object... hashKey){
redisTemplate.opsForHash().delete(key, hashKey);
}
/**
* Hash类型
* 判断缓存中是否有指定key的指定键值对
* @param key 缓存key
* @param hashKey 对应键值对的key
* @return
*/
public Boolean hHashKey(String key, Object hashKey){
return redisTemplate.opsForHash().hasKey(key, hashKey);
}
/**
* Hash类型
* 递增指定key的指定键值对的值,如果不存在,则会创建,并且返回新增后的值
* @param key 缓存key
* @param hashKey 指定键值对的key
* @param delta 递增因子,即要加几
* @return
*/
public double hIncr(String key, String hashKey, long delta) {
return redisTemplate.opsForHash().increment(key, hashKey, delta);
}
/**
* Hash类型
* 递减指定key的指定键值对的值,如果不存在,则会创建,并且返回新增后的值
* @param key 缓存key
* @param hashKey 指定键值对的key
* @param delta 递减因子,即要减几
* @return
*/
public double hDecr(String key, String hashKey, long delta) {
return redisTemplate.opsForHash().increment(key, hashKey, -delta);
}
//================================Set===========================
/**
* Set类型
* 根据key获取Set中的所有值
* @param key 缓存key
* @return
*/
public Set<Object> sGet(String key) {
return redisTemplate.opsForSet().members(key);
}
/**
* Set类型
* 查询指定的key中指定的value是否存在
* @param key 缓存key
* @param value Set中的指定元素
* @return
*/
public Boolean sHasKey(String key, Object value) {
return redisTemplate.opsForSet().isMember(key, value);
}
/**
* Set类型
* 将缓存数据放入指定的key中
* @param key 缓存key
* @param values Set集合
* @return
*/
public Long sSet(String key, Object... values) {
return redisTemplate.opsForSet().add(key, values);
}
/**
* Set类型
* 将缓存数据放入指定的key中,并设置过期时间,单位为秒
* @param key 缓存key
* @param time 过期时间
* @param values 要放入缓存的数据
* @return
*/
public Long sSet(String key, long time, Object... values) {
Long l = sSet(key, values);
expire(key, time);
return l;
}
/**
* Set类型
* 获取指定的key中Set的长度
* @param key 缓存key
* @return
*/
public Long sGetSetLength(String key) {
Long l = redisTemplate.opsForSet().size(key);
return l == null ? 0 : l;
}
/**
* Set类型
* 移除指定的key中指定的value
* @param key 缓存key
* @param values 指定要移除的value
* @return
*/
public long sRemove(String key, Object... values) {
Long l = redisTemplate.opsForSet().remove(key, values);
return l == null ? 0 : l;
}
//==========================List=======================
/**
* List类型
* 获取指定的key中的list集合内容,根据起始下标获取
* @param key 缓存key
* @param start 集合的开始下标,包含在内
* @param end 集合的截止下标,也包含在内
* @return
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* List类型
* 根据key获取list集合的长度
* @param key 缓存key
* @return
*/
public long lGetLength(String key) {
Long l = redisTemplate.opsForList().size(key);
return l == null ? 0 : l;
}
/**
* List类型
* 获取指定key的指定下标的元素
* @param key 缓存key
* @param index 指定的list集合下标
* @return
*/
public Object lGetByIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* List类型
* 将值从左侧放入List类型的缓存中,头插法
* @param key 缓存key
* @param value 要放入缓存的值
* @return
*/
public void lSet(String key, Object value) {
redisTemplate.opsForList().leftPush(key, value);
}
/**
* List类型
* 将值从右侧放入List类型的缓存中,尾插法
* @param key 缓存key
* @param value 要放入缓存的值
*/
public void rSet(String key, Object value) {
redisTemplate.opsForList().rightPush(key, value);
}
/**
* List类型
* 将值从左侧放入List类型的缓存中,头插法,并设置过期时间,单位为秒
* @param key 缓存key
* @param value 要放入缓存的值
* @param time 过期时间
* @return
*/
public void lSet(String key, Object value, long time) {
redisTemplate.opsForList().leftPush(key, value);
expire(key, time);
}
/**
* List类型
* 将值从右侧放入List类型的缓存中,尾插法,并设置过期时间,单位为秒
* @param key 缓存key
* @param value 要放入缓存的值
* @param time 过期时间
*/
public void rSet(String key, Object value, long time) {
redisTemplate.opsForList().rightPush(key, value);
expire(key, time);
}
/**
* List类型
* 将list集合从左边放入缓存,头插法
* @param key 缓存key
* @param list 要放入缓存的集合
*/
public void lSet(String key, List<Object> list) {
redisTemplate.opsForList().leftPushAll(key, list);
}
/**
* List类型
* 将list集合从右边放入缓存,尾插法
* @param key 缓存key
* @param list 要放入缓存的集合
*/
public void rSet(String key, List<Object> list) {
redisTemplate.opsForList().rightPushAll(key, list);
}
/**
* List类型
* 将list集合从左边放入缓存,头插法,并设置过期时间,单位为秒
* @param key 缓存key
* @param list 要放入缓存的集合
* @param time 过期时间
*/
public void lSet(String key, List<Object> list, long time) {
redisTemplate.opsForList().leftPushAll(key, list);
expire(key, time);
}
/**
* List类型
* 将list集合从右边放入缓存,尾插法,并设置过期时间,单位为秒
* @param key 缓存key
* @param list 要放入缓存的集合
* @param time 过期时间
*/
public void rSet(String key, List<Object> list, long time) {
redisTemplate.opsForList().rightPushAll(key, list);
expire(key, time);
}
/**
* List类型
* 修改指定key的list集合中指定下标的值
* @param key 缓存key
* @param index 要修改的值的下标
* @param value 修改的值
*/
public void lUpdateIndex(String key, long index, Object value) {
redisTemplate.opsForList().set(key, index, value);
}
/**
* List类型
* 移除指定key的list集合中指定个数的指定值
* 若count = 0,则表示移除全部
* 若count > 0,则表示从List集合的左侧开始数
* 若count < 0,则表示从List集合的右侧开始数
* @param key 缓存key
* @param count 要移除的元素个数
* @param value 要移除的值
* @return
*/
public Long lRemove(String key, long count, Object value) {
Long l = redisTemplate.opsForList().remove(key, count, value);
return l == null ? 0L : l;
}
//===============================ZSet=============================
/**
* ZSet类型
* 添加ZSet类型的缓存,这是一个有序集合,是按照元素的scores的值由小到大排序的
* @param key 缓存key
* @param value 要添加的值
* @param scores 值的scores,根据此值的大小来排序
*/
public void zAdd(String key, String value, double scores) {
redisTemplate.opsForZSet().add(key, value, scores);
}
/**
* ZSet类型
* 批量添加ZSet类型的缓存,将一个set集合添加的ZSet中
* @param key 缓存key
* @param values 要放入缓存的set集合
*/
public void zAdd(String key, Set<ZSetOperations.TypedTuple<Object>> values) {
redisTemplate.opsForZSet().add(key, values);
}
/**
* ZSet类型
* 移除指定key中ZSet集合中的一个或多个指定的元素
* @param key 缓存key
* @param values 要移除的元素
* @return
*/
public Long zRemove(String key, Object... values) {
Long l = redisTemplate.opsForZSet().remove(key, values);
return l == null ? 0L : l;
}
/**
* ZSet类型
* 增加指定key的ZSet集合中指定元素的scores,并返回增加后的scores值
* @param key 缓存key
* @param value 要增加scores的元素
* @param delta 递增因子,即加几
* @return 增加后的scores值
*/
public Double zincr(String key, String value, double delta) {
return redisTemplate.opsForZSet().incrementScore(key, value, delta);
}
/**
* ZSet类型
* 返回指定key中ZSet集合中指定元素的下标
* @param key 缓存key
* @param value 要获取下标的元素
* @return 指定元素的下标
*/
public Long zRank(String key, Object value) {
return redisTemplate.opsForZSet().rank(key, value);
}
/**
* ZSet类型
* 获取指定key的ZSet集合中指定下标区间的元素,由小到大排序
* @param key 缓存key
* @param start 开始下标,包含在内
* @param end 截止下标,也包含在内
* @return 指定下标区间内的元素
*/
public Set<Object> zRange(String key, long start, long end) {
return redisTemplate.opsForZSet().range(key, start, end);
}
/**
* ZSet类型
* 获取指定key的ZSet集合中指定下标区间的元素,由大到小排序
* @param key 缓存key
* @param start 开始下标,包含在内
* @param end 截止下标,也包含在内
* @return 指定下标区间内的元素
*/
public Set<Object> zReRange(String key, long start, long end) {
return redisTemplate.opsForZSet().reverseRange(key, start, end);
}
/**
* ZSet类型
* 获取指定key的ZSet集合中指定下标区间的元素,并带上元素的scores
* @param key 缓存key
* @param start 开始下标,包含在内
* @param end 截止下标,也包含在内
* @return 指定下标区间内的元素
*/
public Set<ZSetOperations.TypedTuple<Object>> zRangeWithScores(String key, long start, long end) {
return redisTemplate.opsForZSet().rangeWithScores(key, start, end);
}
/**
* ZSet类型
* 获取指定key的ZSet集合中指定scores的元素
* @param key 缓存key
* @param scores 值的scores,根据此值的大小来排序
* @return
*/
public Object zScores(String key, double scores) {
return redisTemplate.opsForZSet().score(key, scores);
}
}
这里获取到真正的token后,前端就要在之后的接口中将这个token放入header中,至于这个header的字段名叫什么,在
这里的配置文件中会配置,配置后,接口的登录校验就会从header中获取这个字段的值来进行登录校验