2.3 解决问题
2.3.1 思路分析
实际上分了两部分,一个是登陆一个是校验。
登陆:①自定义登陆的接口,调用providerManager的方法进行认证
如果认证通过,则生成JWT;把用户信息缓存到redis中。
②自定义UserDetailService,实现查询数据库
校验:①定义JWT认证过滤器,获取token并解析token
②获取token中的userId,从redis中获取用户信息,存入SecurityContextHolder中。
思考:Jwt认证过滤器中从获取到了userId后,怎么获取到完整的用户信息?
2.3.2 准备工作
①添加依赖
<!--redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--fastjson依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.33</version>
</dependency>
<!--jwt依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
②添加Redis相关配置
package utils;
import ...
/**
* Redis使用FastJson序列化
* @author sg
*/
public class FastJsonRedisSerializer<T> implements RedisSerializer<T>{
public static fina1 Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Class<T> clazz;
static{
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
}
public FastJsonRedisSerializer(Class<T> clazz){
super();
this.clazz = clazz;
}
@override
public byte[] serialize(T t) throws SerializationException{
if (t == nu11) {
return new byte[0];
}
return JSON.toJSONString(t,SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
}
@override
public T deserialize(byte[] bytes) throws SerializationException{
if (bytes == nul1 || bytes.length <= 0) {
return nu11;
}
String str =new String(bytes, DEFAULT_CHARSET);
return JSoN.parseobject(str, clazz);
}
protected JavaType getJavaType(Class<?> clazz) {
return TypeFactory.defaultInstance().constructType(clazz);
}
}
package config;
import ...
@configuration
public class RedisConfig {
@Bean
@SuppressWarnings(value = { "unchecked", "rawtypes" })
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class);
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setkeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
// Hash的key也采用StringRedisSerializer的序列化方式
template.setHashkeyserializer(new StringRedisserializer());
template.setHashValueserializer(serializer);
template.afterPropertiesset();
return template;
}
}
③响应类
package domain;
import ...
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseResult<T> {
/**
* 状态码
*/
private Integer code;
/**
* 提示信息,如果有错误时,前端可以获取该字段进行提示
*/
private String msg;
/**
* 查询到的结果数据,
*/
private T data;
public ResponseResult(Integer code, String msg) {
this.code = code;
this.msg =msg;
}
public ResponseResult(Integer code, T data) {
this.code = code;
this.data = data;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg =msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public ResponseResult(Integer code, String msg, T data) {
this.code = code;
this.msg =msg;
this.data = data;
}
}
④工具类
package utils;
import ...
/**
*JWT工具类
*/
public class JwtUtil {
//有效期为
pub1ic static final Long JWT_TTL = 60*60*1000L;// 60*60*1000 一个小时
//设置秘钥明文
public static final String JWT_KEY = "Hello";
public static String getUUID(){
String token = UUID.randomUUID().toString().replaceAl1("-","");
}
/**
* 生成jtw
* @param subject token中要存放的数据(json格式)
* @return
*/
public static String createJWT(String subject) {
JwtBuilder builder =getJwtBuilder(subject,nul11,getuuID());//设置过期时间
return builder.compact();
}
/**
* 生成jtw
* @param subject token中要存放的数据(json格式)
* @param ttlMil1is token超时时间
* @return
*/
public static String createJWT(String subject, Long ttlMillis) {
JwtBuilder builder= getJwtBuilder(subject,tt1Mil1is,getUuID());//设置过期时间
return builder.compact();
}
private static JwtBuilder getJwtBuilder (string subject, Long tt1Millis, string uuid) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
SecretKey secretKey = generalKey();
long nowMi11is = System.currentTimeMi1lis();
Date now = new Date(nowMi11is);
if(tt1Mi11is==nu11) {
tt1Mi11is=JwtUti1.JWT_TTL;
}
long expMillis = nowMillis + ttlMillis;
Date expDate = new Date(expMillis);
return Jwts.builder()
.setId(uuid) //唯一的ID
.setsubject(subject) //主题 可以是JSON数据
.setIssuer("sg") //签发者
.setIssuedAt(now) //签发时间
.signwith(signatureAlgorithm,secretKey) //使用HS256对称加密算法签名,第二个参数为秘钥
.setExpiration (expDate);
}
/**
* 创建token
* @param id
* @param subject
* @param ttlMillis
* @return
*/
public static String createJWT(String id, String subject, Long ttlMillis) {
JwtBuilder builder= getJwtBuilder(subject,ttlMillis,id)://设置过期时间
return builder.compact();
}
public static void main(String[] args) throws Exception {
String token = "eyJhbGci0iJIUzI1NiJ9.eyJqdGkiOiJjYWM2ZDVhZilmNjVlLTQOMDAtYjcxMiOzYWEwOGIyOTIwYjQiLCJzdWIi0iJzZyIsImlzcyI6InNnIiwiaWFOIjoxNjM4MTA2NzEyLCJleHAi0jE2MzgxMTAzMTJ9.JVsSbkP94wuczb4QryQbAke3ysBDIL5ou8fWsbt_ebg";
Claims claims = parseJWT(token);
System.out.println(claims);
}
/**
* 生成加密后的秘钥 secretKey
* @return
*/
public static SecretKey generalKey() {
byte[] encodedkey = Base64.getDecoder().decode(JwtUti1.JWT_KEY);
Secretkey key = new SecretkeySpec(encodedkey, 0, encodedkey.length, "AES");
return key;
}
/**
* 解析
* @param jwt
* @return
* @throws Exception
*/
public static Claims parseJWT(String jwt) throws Exception {
SecretKey secretKey = generalKey();
return Jwts.parser()
.setSigningKey(secretKey)
·parseClaimsJws(jwt)
.getBody();
}
}
package utils;
import ...
@suppresswarnings (value = { "unchecked", "rawtypes" })
@component
public class RedisCache{
@Autowired
public RedisTemplate redisTemplate;
/**
* 缓存基本的对象,Integer、String、实体类等
* @param key缓存的键值
* @param value 缓存的值
*/
public <T> void setcacheobject(final string key, final T value)
redisTemplate.opsForValue().set(key, value);
}
/**
* 缓存基本的对象,Integer、String、实体类等
* @param key 缓存的键值
* @param value 缓存的值
* @param timeout 时间
* @param timeunit 时间颗粒度
*/
public <T> void setCacheobject(final string key, final T value, final Integer timeout, final Timeunit timeUnit) {
redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
}
/**
* 设置有效时间
*
* aparam key Redis键
* @param timeout 超时时间
* areturn true=设置成功;false=设置失败
*/
public boolean expire(final String key, final long timeout){
return expire(key, timeout, TimeUnit.SECONDS);
}
/**
* 设置有效时间
* @param key Redis键
* @param timeout 超时时间
* @param unit 时间单位
* @return true=设置成功;false=设置失败
*/
public boolean expire(final string key, final long timeout, final Timeunit unit){
return redisTemplate.expire(key, timeout, unit);
}
/**
* 获得缓存的基本对象。
* @param key缓存键值
* @return缓存键值对应的数据
*/
public <T> T getCacheobject(final String key){
ValueOperations<String, T> operation = redisTemplate.opsForValue();
return operation.get(key);
}
/**
* 删除单个对象
* @param key
*/
public boolean deleteobject(final String key) {
return redisTemplate.delete(key);
}
/**
* 删除集合对象
* @param collection 多个对象
* @return
*/
public long deleteobject(final collection collection){
return redisTemplate.delete(collection);
}
/**
* 缓存List数据
* aparam key缓存的键值
* @param dataList 待缓存的List数据
* areturn缓存的对象
*/
public <T> long setCachelist(final String key, final List<T> datalist){
Long count = redisTemplate.opsForList(O.rightPushA11(key, datalist);
return count == nul1 ? 0 : count;
}
/**
* 获得缓存的1ist对象
*
* aparam key 缓存的键值
* @return缓存键值对应的数据
*/
public <T> List<T> getCachelList(final String key){
return redisTemplate.opsForList().range(key, 0, -1);
}
/**
* 缓存Set
* @param key缓存键值
* aparam dataset 缓存的数据
* @return缓存数据的对象
*/
public <T> Boundsetoperations<string, T> setCacheset(final String key, final Set<T> dataset) {
BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps (key);
Iterator<T> it = dataset.iterator();
while (it.hasNext()) {
setoperation.add(it.next());
}
return setoperation;
}
/**
* 获得缓存的set
* @param key
* @return
*/
public <T> Set<T> getCacheset(final String key){
return redisTemplate.opsForSet().members(key);
}
/**
* 缓存Map
*
* @param key
* @param dataMap
*/
public <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
if (dataMap !=nu11){
rediTemplate.opsForHash().putAl1(key, dataMap);
}
}
/**
* 获得缓存的Map
* @param key
* @return
*/
public <T> Map<String, T> getCacheMap(final String key){
return redisTemplate.opsForHash().entries(key);
}
/**
* 往Hash中存入数据
* @param key Redis键
* @param hKey Hash键
* @param value 值
*/
public <T> void setCacheMapValue(final String key, final String hkey, final T value) {
redisTemplate.opsForHash().put(key, hkey, value);
}
/**
* 获取Hash中的数据
* @param key Redis键
* @param hKey Hash键
* @return Hash中的对象
*/
public <T> T getCacheMapValue(final String key, final String hkey) {
HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
return opsForHash.get(key, hkey);
}
/**
* 删除Hash中的数据
* @param key
* @param hkey
*/
public void delCacheMapValue(final String key, final String hkey) {
HashOperations hashOperations = redisTemplate.opsForHash();
hashoperations.delete(key, hkey);
}
/**
* 获取多个Hash中的数据
* aparamkey Redis键
* @param hKeys Hash键集合
* @return Hash对象集合
*/
public <T> List<T> getMultiCacheMapValue(final String key, final Collection<0bject> hkeys) {
return redisTemplate.opsForHash().multiGet(key, hkeys);
}
/**
* 获得缓存的基本对象列表
* @param pattern 字符串前缀
* @return对象列表
*/
public Collection<String> keys(final String pattern) {
return redisTemplate.keys(pattern);
}
}
package utils;
import ...
public class WebUtils
/**
* 将字符串渲染到客户端
* aparam response 渲染对象
* @param string待渲染的字符串
* @return null
*/
public static String renderstring(HttpServletResponse response, String string) {
try {
response.setStatus (200);
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
response.getwriter().print(string);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
⑤实体类
package domain;
import ...
/**
* 用户表(user)实体类
*/
@Data
@A11ArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private static fina1 long serialversionUID = -40356785423868312L;
/**
* 主键
*/
private Long id;
/**
* 用户名
*/
private String userName;
/**
* 昵称
*/
private String nickName;
/**
* 密码
*/
private String password;
/**
* 账号状态(0正常1停用)
*/
private String status;
/**
* 邮箱
*/
private String email;
private String email;
/**
* 手机号
*/
private String phonenumber;
/**
* 用户性别(0男,1女,2未知)
*/
private String sex;
/**
* 头像
*/
private String avatar;
/**
* 用户类型(0管理员,1普通用户)
*/
private String userType;
/**
* 创建人的用户id
*/
private Long createBy;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新人
*/
private Long updateBy;
/**
* 更新时间
*/
private Date updateTime;
/**
* 删除标志(0代表未删除,1代表已删除)
*/
private Integer delFlag;
}
评论