登录权限控制是很多系统具备的功能,实现这一功能的方式有很多,其中使用token是现在用的比较多的
好处:可以防止CSRF攻击
用户登录成功后,后台生成一个token秘钥值并存在redis中,同时给此用户的token设置时限,返回一个token给调用者,同时自定义一个@AuthToken注解,被该注解标注的API请求都需要进行token效验,效验通过才可以正常访问,实现接口级的鉴权控制。同时token具有生命周期,在用户持续一段时间不进行操作的话,token则会过期,用户一直操作的话,则不会过期。
pom.xml相关依赖:
org.springframework.boot
spring-boot-starter-data-redis
com.alibaba
fastjson
1.2.58
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 表面需要验证Token的注解
* @author Xuan
* @date 2019/7/28 2:25
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthToken {
}
/**
* Token常量类
* @author Xuan
* @date 2019/7/28 3:08
*/
public final class TokenConstant {
/**
* 设置删除标志为真
*/
public static final Integer DEL_FLAG_TRUE = 1;
/**
* 设置删除标志为假
*/
public static final Integer DEL_FLAG_FALSE = 0;
/**
* redis存储token设置的过期时间,10分钟
*/
public static final Integer TOKEN_EXPIRE_TIME = 60 * 10;
/**
* 设置可以重置token过期时间的时间界限
*/
public static final Integer TOKEN_RESET_TIME = 1000 * 100;
}
/**
* 生成Token的接口
* @author Xuan
* @date 2019/7/28 3:08
*/
public interface TokenGenerator {
/**
* 生成token
* @param strings
* @return
*/
String generate(String... strings);
}
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
/**
* Token生成的实现类
* @author Xuan
* @date 2019/7/28 3:08
*/
@Component
public class TokenGeneratorImpl implements TokenGenerator {
@Override
public String generate(String... strings) {
long timestamp = System.currentTimeMillis();
String tokenMeta = "";
for (String s : strings) {
tokenMeta = tokenMeta + s;
}
tokenMeta = tokenMeta + timestamp;
String token = DigestUtils.md5DigestAsHex(tokenMeta.getBytes());
return token;
}
}
/**
* Redis 配置类
* @author Xuan
* @date 2019/7/28 3:08
*/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
RedisTemplate template = new RedisTemplate();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
import com.xuan.interceptor.AuthorizationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 注册拦截器
* @author Xuan
* @date 2019/7/28 4:42
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
//让bean提前加载
@Bean
public HandlerInterceptor getInterceptor(){
return new AuthorizationInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getInterceptor()).addPathPatterns("/**");
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* Redis工具类
* @author Xuan
* @date 2019/7/28 3:01
*/
@Component
public final class RedisUtil {
@Autowired
private RedisTemplate redisTemplate;
// =============================common============================
/**
* 指定缓存失效时间
* @param key 键
* @param time 时间(秒)
* @return
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
* @param key 可以传一个值 或多个
*/
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
// ============================String=============================
/**
* 普通缓存获取
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
* @param key 键
* @param delta 要增加几(大于0)
* @return
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
* @param key 键
* @param delta 要减少几(小于0)
* @return
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
// ================================Map=================================
/**
* HashGet
* @param key 键 不能为null
* @param item 项 不能为null
* @return 值
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
* @param key 键
* @return 对应的多个键值
*/
public Map
import com.alibaba.fastjson.JSONObject;
import com.xuan.common.redis.RedisUtil;
import com.xuan.common.token.AuthToken;
import com.xuan.common.token.TokenConstant;
import com.xuan.common.token.TokenGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
/**
* 测试token鉴权功能测试类
* @author Xuan
* @date 2019/8/10 16:23
*/
@RestController
public class TestToken {
@Autowired
TokenGenerator tokenGenerator;
@Autowired
private RedisUtil redisUtil;
/**
* 登录的控制方法
* @return
*/
@ResponseBody
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String login() {
//返回的结果数据
JSONObject result = new JSONObject();
//生成token
String token = tokenGenerator.generate("123","456");
//将token存入缓存
redisUtil.set("123",token, TokenConstant.TOKEN_EXPIRE_TIME);
redisUtil.set(token,"123", TokenConstant.TOKEN_EXPIRE_TIME);
result.put("生成的token",token);
result.put("username","123");
return result.toJSONString();
}
/**
* 加了权限注解的方法
* @return
*/
@AuthToken
@ResponseBody
@RequestMapping(value = "/testtoken", method = RequestMethod.GET)
public String testtoken() {
System.out.println("权限通过验证");
return "成功";
}
}