上节我们看到base基础库的base-common的一些基础注解,这些注解后续会有应用。接下来我们看一下base集成的一些内容。
我们都知道查询的时候用的查询条件和排序比较多,因此我定义了两个类来实现根据传入的参数就可实现sql或者hql的拼接。
SortParam实体,用来排序条件的拼装
package com.panda.base.jpa.dao;
public class SortParam {
private Stringkey;
private Orderorder = Order.ASC;
public StringgetKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public OrdergetOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
public SortParam() {
}
public SortParam(String key, Order order) {
this.key = key;
this.order = order;
}
public enum Order {
ASC, DESC;
}
}
QueryParam用来查询条件的sql拼装
package com.panda.base.jpa.dao;
public class QueryParam {
private Stringkey;
private Objectvalue;
private Logiclogic = Logic.Contain;
/**
* 括号内部的连接方式
*/
private LinkModeinnerLinkMode = LinkMode.And;
public QueryParam(String key, Object value, Logic logic) {
this.key = key;
this.value = value;
this.logic = logic;
}
public QueryParam(String key, Object value, Logic logic, LinkMode innerLinkMode) {
this.key = key;
this.value = value;
this.logic = logic;
this.innerLinkMode = innerLinkMode;
}
public QueryParam(String key, Object value) {
this.key = key;
this.value = value;
this.logic = Logic.Equal;
}
public QueryParam() {
}
public LinkModegetInnerLinkMode() {
return innerLinkMode;
}
public void setInnerLinkMode(LinkMode innerLinkMode) {
this.innerLinkMode = innerLinkMode;
}
public StringgetKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public ObjectgetValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
public LogicgetLogic() {
return logic;
}
public void setLogic(Logic logic) {
this.logic = logic;
}
public enum Logic {
Equal,
NotEqual,
Contain,
StartWith,
EndWith,
In,
NotIn,
GreaterThan,
GreaterThanAndEqual,
LessThanAndEqual,
LessThan,
IsNull,
IsNotNull
}
public enum LinkMode {
And, Or;
}
}
再来看看base-util下封装了哪些东西
工具包中含有一些加解密的工具类,另外还加了一个缓存实体和缓存组,缓存组的概念我这里解释一下。
/**
* 概念:
* 组缓存,在传统redis、memcached 这一类的缓存容器中,都是key-value类型的
* 所谓的组缓存,就是在key-value的外层加一个标识
* 用list来做比较,在redis中,list的结构是这样的,并且list的某一项不能设置存活时间:
* abc
* 1
* 2
* 3
*
* 在GroupCache中结构是这样的 :
* abc
* a 1 10
* b 2 5
* c 3 1
* 第一行:a代表key,1代表value,10代表存活10秒,存活时间以秒为单位
*/
CacheEntity
package com.panda.base.utils.cache;
import java.io.Serializable;
/**
* 缓存实体*/
public class CacheEntityimplements Serializable {
private static final long serialVersionUID =2082223810638865724L;
private T key; // key
private V value;// 值
private Longtimestamp;// 缓存的时候存的时间戳,用来计算该元素是否过期
private int expire = Integer.MAX_VALUE; // 默认长期有效
private Groupgroup;// 容器
public CacheEntity(T key, V value, Long timestamp, int expire, Group group) {
this.key = key;
this.value = value;
this.timestamp = timestamp;
this.expire = expire;
this.group = group;
}
public void setTimestamp(Long timestamp) {
this.timestamp = timestamp;
}
public LonggetTimestamp() {
return timestamp;
}
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
public int getExpire() {
return expire;
}
public void setExpire(int expire) {
this.expire = expire;
}
/**
* 获取剩余时间
*
* @return
*/
public int ttl() {
return this.expire - getTime();
}
/**
* 获取当前时间和元素的相差时间(存活时间)
*
* @return
*/
private int getTime() {
Long current = System.currentTimeMillis();
Long value = current -this.timestamp;
return (int) (value /1000) +1;
}
/**
* 是否有效
*
* @return
*/
public boolean isExpire() {
if (getTime() >this.expire) {
// 失效了就移除
group.delete(key);
return false;
}
return true;
}
}
Group
package com.panda.base.utils.cache;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
/**
* 组操作,内部数据有序队列*/
public class Group {
private ArrayBlockingQueue>queue;// 缓存队列
private Integercapacity;
public Group(int capacity) {
queue =new ArrayBlockingQueue<>(capacity);
this.capacity = capacity;
}
/**
* 尾部进。 如果容量已满,则移除距过期时间最短的
*
* @param object
* @param second
*/
public void push(T key, V object, int second) {
// 放入队列
if (!queue.offer(new CacheEntity(key, object, System.currentTimeMillis(), second, this))) {
// 做一次清除
getCacheEntities();
if (!queue.offer(new CacheEntity(key, object, System.currentTimeMillis(), second, this))) {
int ttf = Integer.MAX_VALUE;
CacheEntity maxVtfCacheEntity =null;
Iterator> iterator =queue.iterator();
while (iterator.hasNext()) {
CacheEntity cacheEntity = iterator.next();
if (cacheEntity.ttl() < ttf) {
ttf = cacheEntity.ttl();
maxVtfCacheEntity = cacheEntity;
}
}
if (maxVtfCacheEntity !=null &&this.queue.remove(maxVtfCacheEntity))
queue.offer(new CacheEntity(key, object, System.currentTimeMillis(), second, this));
}
}
}
/**
* 尾部进. 默认失效时间10分钟
*
* @param object
*/
public void push(T key, V object) {
push(key, object, 600);
}
/**
* 返回并移除头部出
*
* @return
*/
public V poll() {
CacheEntity entity =queue.poll();
// 如果有效期超过,返回null
if (!entity.isExpire()) {
return null;
}
return entity.getValue();
}
/**
* 返回头部元素并放到末尾
*
* @return
*/
public V rPoll() {
CacheEntity entity =queue.poll();
// 如果有效期超过,返回null
if (!entity.isExpire()) {
return null;
}
V object = entity.getValue();
queue.offer(entity);
return object;
}
/**
* 通过key寻找有效的缓存实体
*
* @param key
* @return
*/
private CacheEntityfind(T key) {
synchronized (queue) {
Iterator> iterator =queue.iterator();
while (iterator.hasNext()) {
CacheEntity entity = iterator.next();
if (key.equals(entity.getKey())) {
return entity;
}
}
return null;
}
}
/**
* 删除key
*
* @param key
*/
public void delete(T key) {
synchronized (queue) {
CacheEntity entity = find(key);
if (entity !=null) {
queue.remove(entity);
}
}
}
/**
* 根据key获取
*
* @param key
* @return
*/
public V getValue(T key) {
CacheEntity entity = find(key);
if (entity !=null && entity.isExpire()) {
return entity.getValue();
}
return null;
}
/**
* 获取有效的缓存实体
* 移除所有过期的缓存实体。
*
* @return
*/
private List>getCacheEntities() {
List> keys =new ArrayList>();
Iterator> iterator =queue.iterator();
while (iterator.hasNext()) {
CacheEntity cacheEntity = iterator.next();
if (cacheEntity.isExpire()) {
keys.add(cacheEntity);
}
}
return keys;
}
/**
* 获取key列表
*
* @return
*/
public ListgetKeys() {
List keys =new ArrayList();
List> caches = getCacheEntities();
for (CacheEntity cacheEntity : caches) {
keys.add(cacheEntity.getKey());
}
return keys;
}
/**
* 获取值列表
*
* @return
*/
public ListgetValues() {
List values =new ArrayList();
List> caches = getCacheEntities();
for (CacheEntity cacheEntity : caches) {
values.add(cacheEntity.getValue());
}
return values;
}
/**
* 查看元素存活时间,-1 失效,0 长期有效
*
* @param key
* @return
*/
public int ttl(T key) {
CacheEntity entity = find(key);
if (entity !=null) {
return entity.ttl();
}
return -1;
}
/**
* 返回头部的元素
*
* @return
*/
public V peek() {
CacheEntity entity =queue.peek();
if (entity !=null) {
return entity.getValue();
}
return null;
}
/**
* 设置元素存活时间
*
* @param key
* @param second
*/
public void expire(T key, int second) {
CacheEntity entity = find(key);
if (entity !=null) {
entity.setTimestamp(System.currentTimeMillis());
entity.setExpire(second);
}
}
/**
* 查看key是否存在
*
* @param key
* @return
*/
public boolean exist(T key) {
CacheEntity entity = find(key);
if (entity ==null)return false;
return entity.isExpire();
}
/**
* 查看组是否为空
*
* @return
*/
public boolean isEmpty() {
return queue.isEmpty();
}
/**
* 获取存活元素的大小
*
* @return
*/
public int size() {
return getCacheEntities().size();
}
/**
* 获取容量
*
* @return
*/
public IntegergetCapacity() {
return capacity;
}
}
GroupCacheFactory简单的内存缓存实现,现group概念,一个group里面是个有序的集合, 集合支持key-value expire弥补redis list的不足。
package com.panda.base.utils.cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.LinkedHashMap;
import java.util.Map;public class GroupCacheFactory {
private final static Loggerlog = LoggerFactory.getLogger(GroupCacheFactory.class);
// 数据容器
private Mapcontainer;
public GroupCacheFactory() {
container =new LinkedHashMap();
}
/**
* 如果组存在就返回,不存在就创建,保证不为null
*
* @param key
* @return
*/
public Groupgroup(String key, int capacity) {
Group group =null;
Object entry =container.get(key);
if (entry !=null) {
group = (Group) entry;
}else {
group =new Group(capacity);
container.put(key, group);
}
return group;
}
/**
* 如果组存在就返回,不存在就创建,默认容量1000
*
* @param key
* @return
*/
public Groupgroup(String key) {
return this.group(key, 100000);
}
public void clearExpiredCache() {
container.keySet().forEach(key -> {
log.debug("Cache: key=" + key +" size=" + ((Group)container.get(key)).size());
});
}
}
接着就是jwt权限认证的一个实体类和获取token加解密的生成方法。
1.JwtSubject
package com.panda.base.utils.jwt;
import com.alibaba.fastjson.JSON;
import java.time.LocalDateTime;public class JwtSubject {
private LongusId;
private StringuuId;
private StringuserRole;
private StringclientId;
private LocalDateTimeloginTime;
private JwtSubject() {
}
public JwtSubject(Long usId, String userRole, String clientId) {
this(usId, null, userRole, clientId);
}
public JwtSubject(String uuId, String userRole, String clientId) {
this(null, uuId, userRole, clientId);
}
public JwtSubject(Long usId, String uuId, String userRole, String clientId) {
this.usId = usId;
this.uuId = uuId;
this.userRole = userRole;
this.clientId = clientId;
this.loginTime = LocalDateTime.now();
}
public StringgetUuId() {
return uuId;
}
public void setUuId(String uuId) {
this.uuId = uuId;
}
public StringgetUserRole() {
return userRole;
}
public void setUserRole(String userRole) {
this.userRole = userRole;
}
public StringgetClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public LocalDateTimegetLoginTime() {
return loginTime;
}
public void setLoginTime(LocalDateTime loginTime) {
this.loginTime = loginTime;
}
public static JwtSubjecttoSubject(String subStr) {
return JSON.parseObject(subStr, JwtSubject.class);
}
@Override
public StringtoString() {
return JSON.toJSONString(this);
}
public LonggetUsId() {
return usId;
}
public void setUsId(Long usId) {
this.usId = usId;
}
}
根据jwt实体生成token以及对token反解密,JwtUtils
package com.panda.base.utils.jwt;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.InvalidClaimException;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.panda.base.utils.TimeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.UnsupportedEncodingException;
import java.util.Calendar;
import java.util.Date;public class JwtUtils {
private final static Loggerlog = LoggerFactory.getLogger(JwtUtils.class);
/**
* @param userId 用户自增ID,没有可传null
* @param uuId 用户uuId,没有可传null
* @param userRole 用户身份分类,例如: ADMIN,USER
* @param clientId 客户端标识。
* @param expired 过期时间,毫秒。
* @return
*/
public static Stringtoken(Long userId, String uuId, String userRole, String clientId, long expired) {
JwtSubject jwtSubject =new JwtSubject(userId, uuId, userRole, clientId);
return jwtToken(jwtSubject, expired);
}
public static Stringtoken(Long userId, String uuId, String userRole, String clientId) {
return token(userId, uuId, userRole, clientId, TimeUtils.YEAR *10);
}
public final static StringSECRET ="fadffdaldfkjafdja_k32423535";
public static JwtSubjectverify(String jwtToken) {
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).withIssuer("auth0").build();
JWT jwt = (JWT) verifier.verify(jwtToken);
return JwtSubject.toSubject(jwt.getSubject());
}catch (InvalidClaimException e) {
log.error("InvalidClaimException e"+e.getMessage());
}catch (JWTVerificationException e) {
log.error("JWTVerificationException e"+e.getMessage());
}catch (UnsupportedEncodingException e) {
log.error("UnsupportedEncodingException e"+e.getMessage());
}
return null;
}
public static JwtSubjectdecode(String token) {
JWT jwt = JWT.decode(token);
return JwtSubject.toSubject(jwt.getSubject());
}
public static StringjwtToken(JwtSubject subject, long expireTime) {
try {
/* 获取一分钟前的时间,作为token的产生时间,防止服务器时间不一致,导致token校验失败*/
Calendar beforeTime = Calendar.getInstance();
beforeTime.add(Calendar.MINUTE, -1);// 1分钟之前的时间
Date beforeD = beforeTime.getTime();
/* 计算token过期*/
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(calendar.getTimeInMillis() + expireTime);
String token = JWT.create()
.withSubject(subject.toString())
.withIssuedAt(beforeD)
.withExpiresAt(calendar.getTime())
.withIssuer("auth0")
.sign(Algorithm.HMAC256(SECRET));
return token;
}catch (JWTCreationException exception) {
log.error("生成jwtToken异常", exception);
}catch (UnsupportedEncodingException e) {
log.error("生成jwtToken异常", e);
}
return null;
}
public static void main(String[] args) {
System.out.println("long max is " + Long.MAX_VALUE);
System.out.println("int max is " + Integer.MAX_VALUE);
System.out.println("short max is " + Short.MAX_VALUE);
JwtSubject subject =new JwtSubject(32434L, "234242342535435345345345253243", "ANONYMITY", "default");
String token =jwtToken(subject, TimeUtils.YEAR *10);
System.out.println(token);
System.out.println(verify(token));
}
}
运行main方法我们看运行结果
基于这些我们在后边的权限框架中就可以专注业务实现了。
本节内容就到这,下一篇我会讲讲每个业务层的基础封装以及auth权限框架编写sso单点登录系统。源码可以关注“蛋皮皮”公众号回复“大熊猫分布式组件”进行获取。
网友评论