标签
安全
字数
13613 字
阅读时间
72 分钟
java
import com.commnetsoft.commons.Result;
import com.commnetsoft.idm.IdmError;
import com.commnetsoft.idm.util.sso.JwtUtil;
import com.commnetsoft.idm.util.ZIPUtils;
import org.jose4j.jwa.AlgorithmConstraints;
import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.jwk.RsaJwkGenerator;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.lang.JoseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.validation.constraints.NotNull;
import java.security.SecureRandom;
import java.sql.Timestamp;
/**
* Jwt相关能力层
*/
@Service
@RefreshScope
public class JwtService {
private static Logger logger = LoggerFactory.getLogger(JwtService.class);
@Value("#{idmConstantConfig.jwt_secret_key}")
private String jwt_secret_key;
//JWT 最大有效期
@Value("#{idmConstantConfig.jwt_expiration}")
private int jwt_expiration;
//JWT 置换有效期
@Value("#{idmConstantConfig.jwt_replacement}")
private int jwt_replacement;
@Autowired
private LogoutJwtService logoutJwtService;
private static volatile RsaJsonWebKey rsaJsonWebKey;
private static volatile JsonWebSignature jsonWebSignature;
private static volatile JwtConsumer jwtConsumer;
@PostConstruct
public void init(){
synchronized (RsaJsonWebKey.class) {
if (null == rsaJsonWebKey) {
rsaJsonWebKey = rasJwkInstance();
if (null != rsaJsonWebKey) {
jsonWebSignature = jsonWebSignatureInstance();
jwtConsumer = jwtConsumerInstance();
}
}
}
}
public RsaJsonWebKey rasJwkInstance() {
try {
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG","SUN");
secureRandom.setSeed(jwt_secret_key.getBytes("UTF-8"));
RsaJsonWebKey rsaWebKey = RsaJwkGenerator.generateJwk(2048,null,secureRandom);
return rsaWebKey;
} catch (Exception e) {
logger.error("", e);
}
return null;
}
public JsonWebSignature jsonWebSignatureInstance() {
JsonWebSignature jsonWebSignature = new JsonWebSignature();
jsonWebSignature.setKey(rsaJsonWebKey.getPrivateKey());
jsonWebSignature.setKeyIdHeaderValue(rsaJsonWebKey.getKeyId());
jsonWebSignature.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
return jsonWebSignature;
}
public JwtConsumer jwtConsumerInstance() {
JwtConsumer issJwtConsumer = new JwtConsumerBuilder()
.setRequireExpirationTime()
// .setMaxFutureValidityInMinutes(5256000) //设置“exp”索赔在未来可达到的最大值。
.setAllowedClockSkewInSeconds(30)//误差30秒
.setRequireSubject()
.setExpectedAudience(JwtUtil.JWT_AUD_WEB)
.setVerificationKey(rsaJsonWebKey.getPublicKey())
.setJwsAlgorithmConstraints(
new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.WHITELIST, AlgorithmIdentifiers.RSA_USING_SHA256))
.build();
return issJwtConsumer;
}
/**
* 构建一个JWT Token,使用默认时间
* @param accountid 账号id
* @param ip 请求方ip
* @return
* @throws Exception
*/
public String generateJwt(@NotNull String accountid, String ip) throws JoseException {
String jwt = JwtUtil.generateJwt(jsonWebSignature, accountid, ip, jwt_expiration);
return ZIPUtils.compress(jwt);
}
/**
* 构建一个JWT Token
* @param accountid 账号id
* @param ip 请求方ip
* @param expiration jwt有效时长,单位分钟
* @return
* @throws Exception
*/
public String generateJwt(@NotNull String accountid,@NotNull String ip,@NotNull int expiration) throws JoseException {
String jwt = JwtUtil.generateJwt(jsonWebSignature, accountid, ip, expiration);
return ZIPUtils.compress(jwt);
}
/**
* 根据 Consumer还原 jwt 实体
*
* @param jwt
* @return
* @throws InvalidJwtException
*/
public JwtClaims consumerJwt(@NotNull String jwt) throws InvalidJwtException {
return JwtUtil.consumerJwt(jwtConsumer,jwt);
}
/**
* jwt是否超时,超时或无效返回true(弃用)
* @param jwt
* @return
*/
@Deprecated
public boolean isOvertimeJwt(@NotNull String jwt){
try {
//解压
jwt = ZIPUtils.uncompress(jwt);
JwtClaims jwtClaims = consumerJwt(jwt);
Result<Void> isOverTime = JwtUtil.isOvertimeJwt(jwtClaims);
if (!isOverTime.successful()){
return true;
}
return false;
}catch (Exception e){
logger.error("验证jwt是否超时,程序异常:",e);
return true;
}
}
/**
* 根据jwt获取其超时时间戳
* @param jwt
* @return
*/
public Timestamp getExTimeByJwt(@NotNull String jwt){
try {
//解压
jwt = ZIPUtils.uncompress(jwt);
JwtClaims jwtClaims = consumerJwt(jwt);
return JwtUtil.getExTimeByJwt(jwtClaims);
} catch (Exception e){
logger.error("根据jwt获取其超时时间戳,程序异常:",e);
return null;
}
}
/**
* 验证JWT token是否有效(不对Jwt失效做检测)
* @param jwt
* @param ip
* @return
*/
public Result<Void> verifyJwtNotime(@NotNull String jwt, String ip) {
jwt = ZIPUtils.uncompress(jwt);
if(logoutJwtService.isInvalidJwt(jwt)){
return Result.create(IdmError.verify_token_fail);
}
try {
JwtClaims jwtClaims = consumerJwt(jwt);
return JwtUtil.verifyJwtNoTime(jwtClaims, ip);
} catch (Exception e){
logger.error("验证JWT token是否有效,程序异常:",e);
return Result.create(IdmError.verify_token_illegality);
}
}
/**
* 验证JWT token是否有效
* @param jwt
* @param ip 请求方ip
* @return
*/
public Result<Void> verifyJwt(@NotNull String jwt, String ip) {
jwt = ZIPUtils.uncompress(jwt);
if(logoutJwtService.isInvalidJwt(jwt)){
return Result.create(IdmError.verify_token_fail);
}
try {
JwtClaims jwtClaims = consumerJwt(jwt);
return JwtUtil.verifyJwt(jwtClaims, ip);
} catch (Exception e){
logger.error("验证JWT token是否有效,程序异常:",e);
return Result.create(IdmError.verify_token_illegality);
}
}
/**
* 根据jwt返回账户id(与verifyJwt区别仅为返回对象不一致)
* @param jwt
* @param ip 请求方ip
* @return
*/
public Result<String> getAccountidByJwt(@NotNull String jwt, String ip){
jwt = ZIPUtils.uncompress(jwt);
if(logoutJwtService.isInvalidJwt(jwt)){
return Result.create(IdmError.verify_token_fail);
}
try {
JwtClaims jwtClaims = consumerJwt(jwt);
return JwtUtil.getAccountidByJwt(jwtClaims, ip);
} catch (Exception e){
logger.error("根据jwt返回账户id,程序异常:",e);
return Result.create(IdmError.verify_token_illegality);
}
}
/**
* 检测JWT是否在置换时间内
* @param jwt
* @return 允许置换jwt,返回相关用户id
*/
public Result<String> replaceJwtTime(@NotNull String jwt){
try {
//解压
jwt = ZIPUtils.uncompress(jwt);
JwtClaims jwtClaims = consumerJwt(jwt);
return JwtUtil.isinreplace(jwtClaims,jwt_replacement);
}catch (Exception e){
logger.error("验证jwt是否在置换时间内,程序异常:",e);
return Result.create(IdmError.verify_token_illegality);
}
}
}java
import com.alibaba.fastjson.JSONObject;
import com.commnetsoft.commons.Result;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.data.constant.Entity;
import com.commnetsoft.data.model.Data;
import com.commnetsoft.idm.IdmError;
import com.commnetsoft.idm.constants.AccountProperty;
import com.commnetsoft.idm.control.v2.model.TokenLoginVo;
import com.commnetsoft.idm.model.sso.AbstractLoginPoJo;
import com.commnetsoft.idm.model.sso.ILoginPoJo;
import com.commnetsoft.idm.service.data.DataApi;
import com.commnetsoft.idm.util.am.UserUtil;
import com.commnetsoft.idm.util.ZIPUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.validation.constraints.NotNull;
import java.sql.Timestamp;
/**
* 账户登录登出能力层
*/
@Service
public class LoginoutService {
private static Logger logger = LoggerFactory.getLogger(LoginoutService.class);
@Autowired
private JwtService jwtService;
@Autowired
private LogoutJwtService logoutJwtService;
@Autowired
private DataApi dataApi;
@Autowired
private UserUtil userUtil;
/**
* 登录总入口选择登录相关实现类(输出json_web_token)
* @param login
* @param loginip
* @return
*/
public Result<TokenLoginVo> login(@NotNull JSONObject login, String loginip){
AbstractLoginPoJo abstractLoginPoJo = JSONObject.parseObject(login.toJSONString(),AbstractLoginPoJo.class);
ILoginVali iLoginVali = LoginValiFactory.getLoginVali(abstractLoginPoJo.getLoginType());
Result<ILoginPoJo> creatmodelresult = iLoginVali.creatLoginModel(login,loginip,null);
if (!creatmodelresult.successful()){
return Result.create(IdmError.login_transform_fail);
}
Result<String> loginvali = iLoginVali.loginVali(creatmodelresult.tryData());
//账密匹配失败,直接返回错误码
if (!loginvali.successful()){
return Result.create(loginvali);
}
//账密匹配成功,开始生成jwt数据输出
String accountid = loginvali.tryData();
try {
String jwt = jwtService.generateJwt(accountid,loginip);
//查询账户信息
Data accountdata = dataApi.get(Entity.account.getValue(),accountid,null).tryData();
TokenLoginVo tokenLoginVo = packLoginVo(accountdata,jwt);
return Result.create(tokenLoginVo);
} catch (Exception e){
logger.error("用户认证 - 账密登录,根据账号id与ip生成jwt异常,accountid={},ip={}",accountid,loginip,e);
return Result.create(IdmError.jwt_generate_error);
}
}
/**
* 根据账号模型数据及jwt生成用户登录返回对象
* @param accountdata
* @return
*/
public TokenLoginVo packLoginVo(@NotNull Data accountdata,@NotNull String jwt){
TokenLoginVo tokenLoginVo = new TokenLoginVo();
tokenLoginVo.setAccess_Token(jwt);
tokenLoginVo.setUid(accountdata.getId());
String showname = userUtil.dealShowname(accountdata);
if (StringUtils.isNotBlank(showname)){
tokenLoginVo.setShowname(showname);
}
String mobilephone = accountdata.getString(AccountProperty.mobilephone);
if (StringUtils.isNotBlank(mobilephone)){
tokenLoginVo.setMobilephone(UserUtil.dealMobile(mobilephone));
}
String username = accountdata.getString(AccountProperty.username);
if (StringUtils.isNotBlank(username)){
tokenLoginVo.setUsername(UserUtil.dealUsername(username));
}
String idcard = accountdata.getString(AccountProperty.idcard);
if (StringUtils.isNotBlank(idcard)){
tokenLoginVo.setIdcard(UserUtil.dealIdcard(idcard));
}
return tokenLoginVo;
}
/**
* 根据失效jwt(指定时间内)延期jwt
* @param jwt
* @param ip
* @return
*/
public Result<TokenLoginVo> replaceByJwt(@NotNull String jwt, String ip){
//验证票据是否有效,不对jwt
Result<Void> verifyresult = jwtService.verifyJwtNotime(jwt, ip);
if (!verifyresult.successful()){
//票据无效,直接返回报错
return Result.create(verifyresult);
}
Result<String> replaceresult = jwtService.replaceJwtTime(jwt);
if (!replaceresult.successful()){
//票据在重置时间点以外,直接返回报错
return Result.create(replaceresult);
}
//允许置换jwt,开始生成jwt数据输出
String accountid = replaceresult.tryData();
try {
String newjwt = jwtService.generateJwt(accountid,ip);
//查询账户信息
Data accountdata = dataApi.get(Entity.account.getValue(),accountid,null).tryData();
TokenLoginVo tokenLoginVo = packLoginVo(accountdata,newjwt);
return Result.create(tokenLoginVo);
} catch (Exception e){
logger.error("用户认证 - 延期jwt,根据账号id与ip生成jwt异常,accountid={},ip={}",accountid,ip,e);
return Result.create(IdmError.verify_token_illegality);
}
}
/**
* 根据jwt登出账户(使Jwt无效化)
* @param jwt
* @return
*/
public Result<Void> logoutByJwt(@NotNull String jwt, String ip){
//验证票据是否有效
Result<Void> verifyresult = jwtService.verifyJwt(jwt, ip);
if (!verifyresult.successful()){
//票据无效,无需告知直接返回登出成功即可
return Result.create();
}
Timestamp jwtExtime = jwtService.getExTimeByJwt(jwt);
logoutJwtService.insertJwt(ZIPUtils.uncompress(jwt),jwtExtime);
logger.info("根据jwt登出账户成功,jwt:{}",jwt);
return Result.create();
}
}java
import com.commnetsoft.core.CommonError;
import com.commnetsoft.db.service.AbsService;
import com.commnetsoft.exception.MicroRuntimeException;
import com.commnetsoft.idm.dao.sso.LogoutJwtDao;
import com.commnetsoft.idm.model.sso.LogoutJwtPo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.sql.Timestamp;
/**
* 登出JWT能力层
*/
@Service
public class LogoutJwtService extends AbsService<LogoutJwtPo, Integer> {
private static final Logger logger = LoggerFactory.getLogger(LogoutJwtService.class);
@Autowired
private LogoutJwtDao logoutJwtDao;
@Override
protected LogoutJwtDao getBaseDao() {
return logoutJwtDao;
}
/**
* 插入Jwt与过期时间映射关系
*
* @param jwt
*/
public void insertJwt(String jwt,Timestamp expTime) {
try {
LogoutJwtPo logoutJwtPo =new LogoutJwtPo();
logoutJwtPo.setJwt(jwt);
logoutJwtPo.setExpirationtime(expTime);
logoutJwtDao.insert(logoutJwtPo);
} catch (Exception e) {
logger.error("插入JWT与过期时间映射关系失败", e);
throw new MicroRuntimeException(CommonError.internal_error, e);
}
}
/**
* 判断Jwt是否已经无效(防止使用未过期但是已经失效jwt重新登录验证)
*
* @param jwt
* @return true:已失效 false:未失效
*/
public Boolean isInvalidJwt(String jwt) {
LogoutJwtPo logoutJwtPo = new LogoutJwtPo();
logoutJwtPo.setJwt(jwt);
Long count=super.queryCount(logoutJwtPo);
if(count>0){
return true;
}
return false;
}
/**
* 任务调度执行删除过期的JWT
*/
public void deleteExpirationtimeJwt() {
try {
logoutJwtDao.deleteExpirationtimeJwt(new Timestamp(System.currentTimeMillis()));
} catch (Exception e) {
logger.error("删除过期JWT失败");
throw new MicroRuntimeException(CommonError.internal_error, e);
}
}
}java
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.idm.model.sso.TicketPo;
import com.commnetsoft.idm.model.sso.TokenPo;
import com.commnetsoft.idm.util.UUIDGenerator;
import com.commnetsoft.idm.util.sso.TicketCacheUtil;
import com.commnetsoft.idm.util.sso.TokenCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* @ClassName TicketService
* @Author yzg
* @Date 2020/11/11
* @Version 1.0
*/
@Service
public class TicketService {
private static final Logger logger = LoggerFactory.getLogger(TicketService.class);
/**
* ticket的命名空间
*/
public static final String NAMESPACE_TICKET = "ticket";
@Value("#{idmConstantConfig.ssotickettime}")
private Integer ssotickettime;
@Autowired
private TokenService tokenService;
@Autowired
private TokenCache tokenCache;
@Autowired
private TicketCacheUtil ticketCacheUtil;
/**
* 删除票据
* @param key
* @return ticket
*/
public void remove(String key){
//判断是否是票据
if(isTicket(key)){
ticketCacheUtil.destroyTicket(key);
}
}
/**
* 获取票据
* @param key
* @return ticket
*/
public TicketPo get(String key){
TicketPo ticket = null;
if(key == null){
return ticket;
}
//判断是否是票据
if(isTicket(key)){
//返回票据
ticket = ticketCacheUtil.getTicket(key);
}
return ticket;
}
/**
* 根据令牌生成票据
* @param token 令牌
* @param appid 所属资源(如果为空则,表示没有所属权,即所有系统可以使用)
* @return
*/
public TicketPo createTicket(TokenPo token, Integer appid){
TicketPo ticket = new TicketPo();
if( null == token){
return ticket;
}
String id = UUIDGenerator.getInstance().generate()+"-"+NAMESPACE_TICKET;
ticket.setId(id);
ticket.setUid(token.getUid());
ticket.setAppid(token.getAppid());
ticket.setIp(token.getIp());
ticket.setAppid(appid);
//设置开始时间
long creatTime = System.currentTimeMillis();
ticket.setCreatetime(new Date(creatTime));
//设置结束时间
long expiresTime = creatTime + (TimeUnit.MINUTES.toSeconds(ssotickettime)*1000);
ticket.setExpires(new Date(expiresTime));
//添加到缓存
add(ticket);
// updateTokenExpires(token); //暂不做access_token延长时长操作 updateTokenExpires(token);
return ticket;
}
/**
* 判断是否是ticket,ticket返回true,否则返回false
* @param key
* @return
*/
public boolean isTicket(String key){
if(StringUtils.isNotBlank(key) && key.endsWith(NAMESPACE_TICKET)){
return true;
}else{
return false;
}
}
/**新增票据
* @param ticket
*/
public void add(TicketPo ticket){
if(ticket == null){
return;
}else{
if(!isTicket(ticket.getId())){
//令牌:保存缓存和数据库
tokenService.insert(ticket);
tokenCache.putToken(ticket.getId(),ticket);
}else{
//票据:保存缓存
ticketCacheUtil.putTicket(ticket.getId(),ticket);
}
}
}
}java
import com.commnetsoft.commons.Result;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.db.service.AbsService;
import com.commnetsoft.idm.IdmError;
import com.commnetsoft.idm.dao.sso.TokenDao;
import com.commnetsoft.idm.model.sso.SsoCodeBo;
import com.commnetsoft.idm.model.sso.TokenPo;
import com.commnetsoft.idm.util.UUIDGenerator;
import com.commnetsoft.idm.util.sso.TokenCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* @ClassName TokenService
* @Author wyj
* @Date 2020/9/2 0002 14:47
* @Version 1.0
*/
@Service
public class TokenService extends AbsService<TokenPo, String> {
private static final Logger logger = LoggerFactory.getLogger(TokenService.class);
public final static Long MINUTE_30 = TimeUnit.MINUTES.toMillis(30);
public final static Long HOURS_12 = TimeUnit.HOURS.toMillis(12);
public final static Long HOURS_2 = TimeUnit.HOURS.toMillis(2);
@Autowired
private TokenDao tokenDao;
@Autowired
private TokenCache tokenCache;
@Value("#{idmConstantConfig.token_expiration}")
private Integer token_expiration;
@Value("#{idmConstantConfig.refresh_token_expiration}")
private Integer refresh_token_expiration;
@Override
protected TokenDao getBaseDao() {
return tokenDao;
}
/**
* 新增票据
* @param token
*/
public void add(TokenPo token){
if(token == null){
return;
}
if(isToken(token.getId())){
//令牌:保存缓存和数据库
insert(token);
tokenCache.putToken(token.getId(),token);
}
}
/**
* 判断是否是token,token返回true,否则返回false
* @param key
* @return
*/
public boolean isToken(String key){
if(org.apache.commons.lang3.StringUtils.isNotBlank(key) && key.endsWith(TokenPo.ACCESS_TOKEN_NAMESPACE)){
return true;
}else{
return false;
}
}
/**
* 判断是否是refreshToken,refreshToken返回true,否则返回false
* @param key
* @return
*/
public boolean isrefreshToken(String key){
if(org.apache.commons.lang3.StringUtils.isNotBlank(key) && key.endsWith(TokenPo.REFRESH_TOKEN_NAMESPACE)){
return true;
}else{
return false;
}
}
/**
* 创建Token(根据生成的第三方授权码生成)
* @param ssoCodeBo 授权码实体类
* @return
*/
public TokenPo createToken(SsoCodeBo ssoCodeBo){
if (null == ssoCodeBo){
return null;
}
TokenPo token = new TokenPo();
token.setUid(ssoCodeBo.getUid());
token.setAppid(ssoCodeBo.getAppid());
TokenPo resultToken = queryOne(token);
if (null != resultToken && isInvalidToken(resultToken)){
token = resultToken;
//延长token时长
//暂不做access_token延长时长操作 updateTokenExpires(token);
} else {
//创建token
String id = UUIDGenerator.getInstance().generate() + "-" + TokenPo.ACCESS_TOKEN_NAMESPACE;
token.setId(id);
//创建刷新票据id
String refresh_token = UUIDGenerator.getInstance().generate() + "-" + TokenPo.REFRESH_TOKEN_NAMESPACE;
token.setRefresh_token(refresh_token);
//设置开始时间
long creatTime = System.currentTimeMillis();
token.setCreatetime(new Date(creatTime));
token.setIp(ssoCodeBo.getIp());
//设置结束时间
//long expiresTime = creatTime + (TicketCache.Token.getTime()*1000);
long expiresTime = creatTime + (TimeUnit.MINUTES.toSeconds(token_expiration)*1000);
token.setExpires(new Date(expiresTime));
long refresh_expiresTime = creatTime + (TimeUnit.MINUTES.toSeconds(refresh_token_expiration)*1000);
token.setRefresh_expires(new Date(refresh_expiresTime));
//添加token
add(token);
}
return token;
}
/**
* 创建Token
* @param accountid
* @param appid
* @param userip
* @return
*/
public TokenPo createToken(String accountid, Integer appid, String userip){
if (StringUtils.isBlank(accountid) || null == appid || StringUtils.isBlank(userip)) {
return null;
}
TokenPo token = new TokenPo();
token.setUid(accountid);
token.setAppid(appid);
TokenPo resultToken = queryOne(token);
if (null != resultToken && isInvalidToken(resultToken)){
token = resultToken;
//延长token时长
//暂不做access_token延长时长操作 updateTokenExpires(token);
} else {
//创建token
String id = UUIDGenerator.getInstance().generate() + "-" + TokenPo.ACCESS_TOKEN_NAMESPACE;
token.setId(id);
//创建刷新票据id
String refresh_token = UUIDGenerator.getInstance().generate() + "-" + TokenPo.REFRESH_TOKEN_NAMESPACE;
token.setRefresh_token(refresh_token);
//设置开始时间
long creatTime = System.currentTimeMillis();
token.setCreatetime(new Date(creatTime));
token.setIp(userip);
//设置结束时间
//long expiresTime = creatTime + (TicketCache.Token.getTime()*1000);
long expiresTime = creatTime + (TimeUnit.MINUTES.toSeconds(token_expiration)*1000);
token.setExpires(new Date(expiresTime));
long refresh_expiresTime = creatTime + (TimeUnit.MINUTES.toSeconds(refresh_token_expiration)*1000);
token.setRefresh_expires(new Date(refresh_expiresTime));
//添加token
add(token);
}
return token;
}
/**
* 根据刷新令牌获取新access_token
* @param refreshToken
* @param appid
* @param userip
* @return
*/
public Result<TokenPo> refreshToken(String refreshToken, Integer appid, String userip){
if (StringUtils.isBlank(refreshToken)){
return Result.create(IdmError.token_refreshtoken_null);
}
//判断是否为刷新令牌
if (!isrefreshToken(refreshToken)) {
return Result.create(IdmError.token_refreshtoken_error);
}
TokenPo tokenPo = queryByrefreshToken(refreshToken);
if (null == tokenPo){
return Result.create(IdmError.token_refreshtoken_outtime);
}
//检测是否符合替换token条件
if (!tokenPo.getAppid().equals(appid)) {
return Result.create(IdmError.token_appid_error);
}
long nowTime = System.currentTimeMillis();
long expiresTime = tokenPo.getExpires().getTime();
//未到token失效时间(失效时间前30分钟予以更换),不予以更换
if (nowTime > (expiresTime - (TimeUnit.MINUTES.toSeconds(30)*1000))) {
return Result.create(IdmError.token_expiresTime_notarrived);
}
long refresh_expiresTime = tokenPo.getRefresh_expires().getTime();
if (nowTime < refresh_expiresTime) {
return Result.create(IdmError.token_refreshexpires_outtime);
}
//TODO ip校验,目前暂不对用户ip进行相关校验
//生成全新access_token
//创建token
TokenPo token = new TokenPo();
String id = UUIDGenerator.getInstance().generate() + "-" + TokenPo.ACCESS_TOKEN_NAMESPACE;
token.setId(id);
//创建刷新票据id
String refresh_token = UUIDGenerator.getInstance().generate() + "-" + TokenPo.REFRESH_TOKEN_NAMESPACE;
token.setRefresh_token(refresh_token);
token.setUid(tokenPo.getUid());
token.setAppid(appid);
//设置开始时间
long creatTime = System.currentTimeMillis();
token.setCreatetime(new Date(creatTime));
token.setIp(userip);
//设置结束时间
long newexpiresTime = creatTime + (TimeUnit.MINUTES.toSeconds(token_expiration)*1000);
token.setExpires(new Date(newexpiresTime));
long newrefresh_expiresTime = creatTime + (TimeUnit.MINUTES.toSeconds(refresh_token_expiration)*1000);
token.setRefresh_expires(new Date(newrefresh_expiresTime));
//添加token
add(token);
//清理过期token
remove(tokenPo.getId());
return Result.create(token);
}
/**
* 根据access_token删除清理token对象
* @param access_token
* @return
*/
public Result<Void> deleteToken(String access_token){
if (StringUtils.isBlank(access_token)) {
return Result.create(IdmError.auth_accessToken_null);
}
if (!isToken(access_token)){
return Result.create(IdmError.auth_accessToken_error);
}
TokenPo accessToken = queryByKey(access_token);
if (null == accessToken || !isInvalidToken(accessToken)){
return Result.create(IdmError.auth_accessToken_outtime);
}
remove(access_token);
return Result.create();
}
/**
* 根据刷新token令牌,查询Token对象
* @param refreshToken
* @return
*/
public TokenPo queryByrefreshToken(String refreshToken){
if (StringUtils.isBlank(refreshToken)){
return null;
}
TokenPo query = new TokenPo();
query.setRefresh_token(refreshToken);
return queryOne(query);
}
/**
* 验证票据是否已经超时失效. true 有效 false 超时失效
* @param token
* @return
*/
public boolean isInvalidToken(TokenPo token){
if(token == null || token.getExpires() == null){
return false;
}
Date date = token.getExpires();
long currentTime = System.currentTimeMillis();
long value = date.getTime() - currentTime;
if(value < 0){
//如果已经超时失效则移除令牌
remove(token.getId());
return false;
}
return true;
}
/**
* 删除票据
* @param key tokenid
* @return
*/
public void remove(String key){
if(key == null){
return;
}
//判断是否是票据
if(isToken(key)){
//令牌:数据库中删除同时缓存中也删除
int i = deleteByKey(key);
if(i > 0 ){
tokenCache.deleteToken(key);
}
}
}
/**
* 更新令牌的有效时间。
* 默认2小时有效, 当离过期时间只有半小时,所有操作将对令牌进行延期操作。最长可延时8小时。
*
* 令牌延期功能弃用,不做延期操作
* @param token
*/
@Deprecated
public void updateTokenExpires(TokenPo token){
Long now = System.currentTimeMillis();
Long expires = token.getExpires().getTime();
Long create = token.getCreatetime().getTime();
//未过期,离过期时间只有半小时且未满8小时进行延期。
if((expires > now) && ((expires - now) < MINUTE_30) && ((now - create) < HOURS_12)){
//延期操作
token.setExpires(new Date(now+HOURS_2));
TokenPo t = new TokenPo();
t.setId(token.getId());
t.setExpires(token.getExpires());
//更新缓存中的令牌
tokenCache.putToken(token.getId(),token);
//更新数据库中的令牌超时时间
updateByKeySelective(t);
}
}
}java
package com.commnetsoft.idm.util.am;
import com.commnetsoft.commons.Result;
import com.commnetsoft.commons.utils.DateUtils;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.core.utils.TemplateUtil;
import com.commnetsoft.idm.IdmError;
import com.commnetsoft.idm.constants.IdmConstant;
import com.commnetsoft.idm.model.am.PwdRulePo;
import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.util.Assert;
import javax.validation.constraints.NotNull;
import java.util.*;
@RefreshScope
public class PwdRuleUtil {
private static final Logger logger = LoggerFactory.getLogger(PwdRuleUtil.class);
private static PwdRuleUtil pwdRuleUtil = new PwdRuleUtil();
//特殊字符字典
static char[] ch=new char[]{'!','@','~','#','$','%','^','&','*','(',')','_','+','-','=',',','.','/','<','>','?',';',':','"','{','}','[',']','|'};
static char[] LETTER=new char[]{'A','B','C','D','E','F','G','H','J','K','L','M','N','P','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','j','k','m','n','p','r','s','t','u','v','w','x','y','z'};
static char[] UPPER=new char[]{'A','B','C','D','E','F','G','H','J','K','L','M','N','P','R','S','T','U','V','W','X','Y','Z'};
static char[] LOWWER=new char[]{'a','b','c','d','e','f','g','h','j','k','m','n','p','r','s','t','u','v','w','x','y','z'};
static char[] DIGHT=new char[]{'2','3','4','5','6','7','8','9'};
private PwdRuleUtil() {
}
public static PwdRuleUtil getInstance() {
return pwdRuleUtil;
}
public boolean reachLocked(@NotNull PwdRulePo pwdRulePo, int errnum) {
Assert.notNull(pwdRulePo, "密码策略不能为空");
if (!isLockEnable(pwdRulePo)) {
return false;
}
Integer num = pwdRulePo.getLocknum();
if (num == null || num < 1) {
return false;
}
return errnum >= num;
}
public boolean isLockEnable(@NotNull PwdRulePo pwdRulePo) {
Assert.notNull(pwdRulePo, "密码策略不能为空");
return IdmConstant.TRUE.equals(pwdRulePo.getLockenable());
}
public Date getUnLockedTime(@NotNull PwdRulePo pwdRulePo) {
Assert.notNull(pwdRulePo, "密码策略不能为空");
return DateUtils.addMinutes(new Date(), pwdRulePo.getLocktime());
}
/**
* 校验密码是否满足策略要求
* <ul>
* <li>密码长度验证</li>
* <li>密码只能有字符 数字 特殊字符</li>
* <li>密码必须包含字符种类验证</li>
* <li>密码连续数验证</li>
* <li>密码重复数验证</li>
* </ul>
*
* @param pwdRulePo
* @param password
* @return
*/
public Result<Void> validte(@NotNull PwdRulePo pwdRulePo, @NotNull String password, String loginname) {
Assert.notNull(pwdRulePo, "密码策略不能为空");
Assert.notNull(password, "密码不能为空");
// 密码长度验证
Result<Void> result = isLength(pwdRulePo, password);
if (!result.successful()) {
return result;
}
// 密码只能有字符 数字 特殊字符
if (!StringUtils.isAsciiPrintable(password)) {
return Result.create(IdmError.password_illegality);
}
// 密码必须包含字符种类验证
result = isMust(pwdRulePo, password);
if (!result.successful()) {
return result;
}
// 密码连续数验证
result = isSerialNum(pwdRulePo, password);
if (!result.successful()) {
return result;
}
// 密码重复数验证
result = isRepeatnum(pwdRulePo, password);
if (!result.successful()) {
return result;
}
//判断账号与密码相同
result = isEqLoginname(pwdRulePo, loginname, password);
if (!result.successful()) {
return result;
}
return result;
}
/**
* 判断账号与密码相同规则
* @param pwdRulePo
* @param loginname
* @param password
* @return
*/
private Result<Void> isEqLoginname(@NotNull PwdRulePo pwdRulePo, String loginname, String password){
if (null == pwdRulePo){
logger.error("判断账号与密码相同规则,密码规则实体类为空");
return Result.create(IdmError.fail);
}
if(IdmConstant.TRUE.equals(pwdRulePo.getEqloginname()) && StringUtils.equals(loginname,password)){
return Result.create(IdmError.password_eqloginname);
}
return Result.create();
}
/**
* 是否包含必填字符
* 可配置: 数值 字母 大写字母 小写字母 特殊字符
* 策略保存时,应保证数据合法性(如:字母与大小写字母不能同时设置,否则以字母为准), 此处不做校验
*
* @param pwdRulePo
* @param password
* @return
*/
private Result<Void> isMust(PwdRulePo pwdRulePo, String password) {
Integer mustValue = pwdRulePo.getMust();
Integer ismustVlaue = pwdRulePo.getIsmust();
Integer pwdkindnumVlaue = pwdRulePo.getPwdkindnum();
Integer nowMust = 0;
Integer pwdkind = 0;
char[] chs = password.toCharArray();
// 向nowMust中写入所包含的密码的种类
for (char c : chs) {
if (c <= '9' && c >= '0') {
nowMust = nowMust | PwdRulePo.MUST_DIGIT;
} else if (c <= 'z' && c >= 'a') {
nowMust = nowMust | PwdRulePo.MUST_LOWERCASE;
nowMust = nowMust | PwdRulePo.MUST_LETTERCASE;
} else if (c <= 'Z' && c >= 'A') {
nowMust = nowMust | PwdRulePo.MUST_UPPERCASE;
nowMust = nowMust | PwdRulePo.MUST_LETTERCASE;
} else {
nowMust = nowMust | PwdRulePo.MUST_SPECIAL;
}
}
// 不在勾选列表的种类不计数
// 数字
if((nowMust & PwdRulePo.MUST_DIGIT) == PwdRulePo.MUST_DIGIT && (mustValue & PwdRulePo.MUST_DIGIT) == PwdRulePo.MUST_DIGIT){
pwdkind++;
}
// 字母
if((nowMust & PwdRulePo.MUST_LETTERCASE) == PwdRulePo.MUST_LETTERCASE && (mustValue & PwdRulePo.MUST_LETTERCASE) == PwdRulePo.MUST_LETTERCASE){
pwdkind++;
}
// 小写字母
if((nowMust & PwdRulePo.MUST_LOWERCASE) == PwdRulePo.MUST_LOWERCASE && (mustValue & PwdRulePo.MUST_LOWERCASE) == PwdRulePo.MUST_LOWERCASE){
pwdkind++;
}
// 大写字母
if((nowMust & PwdRulePo.MUST_UPPERCASE) == PwdRulePo.MUST_UPPERCASE && (mustValue & PwdRulePo.MUST_UPPERCASE) == PwdRulePo.MUST_UPPERCASE){
pwdkind++;
}
// 特殊字符
if((nowMust & PwdRulePo.MUST_SPECIAL) == PwdRulePo.MUST_SPECIAL && (mustValue & PwdRulePo.MUST_SPECIAL) == PwdRulePo.MUST_SPECIAL){
pwdkind++;
}
// 当前包含的字符串 与 包含、必须包含、种类的字符串做比较
// 当包含必须的字符串, 且 包含的字符串类型数量大于等于至少的值 表示验证成功
if((nowMust & ismustVlaue) == ismustVlaue && pwdkind >= pwdkindnumVlaue){
return Result.create();
}
// 验证失败, 提供密码策略提示信息
String message = mustMessage(pwdRulePo);
Result<Void> result = Result.create(IdmError.password_must_illegality);
result.setMessage(message);
result.setDesc(message);
return result;
}
/**
* 长度是否符合密码策略
*
* @param pwdRulePo
* @param password
* @return
*/
private Result<Void> isLength(PwdRulePo pwdRulePo, String password) {
Integer lengthMin = pwdRulePo.getLengthmin();
Integer lengthMax = pwdRulePo.getLengthmax();
int passwordLength = password.length();
if (passwordLength < lengthMin || passwordLength > lengthMax) {
Map<String, Object> map = new HashMap<>(4);
map.put("lengthmin", lengthMin);
map.put("lengthmax", lengthMax);
Result<Void> result = Result.create(IdmError.password_length_illegality);
result.setMessage(TemplateUtil.buildTemplate(result.getMessage(), map));
result.setDesc(result.getMessage());
return result;
}
return Result.create();
}
/**
* 密码连续字符个数验证
* abc算连续3个 aba算连续2个
*
* @param pwdRulePo
* @param password
* @return
*/
private Result<Void> isSerialNum(PwdRulePo pwdRulePo, String password) {
char[] chs = password.toCharArray();
// 存放最大连续值的变量
int max = 2;
// 临时存储变量
int temp = 1;
// 判断递增还是递减 ,默认为递增
boolean increment = true;
for (int i = 0; i < chs.length - 1; i++) {
char c = chs[i];
if ((c <= '9' && c >= '0') || (c <= 'Z' && c >= 'A')|| (c <= 'z' && c >= 'a')) {
if ((chs[i] + 1 == chs[i + 1])) {
// 在指定范围内按照ascii码值递增顺序来判断字符连续
if (false == increment) {
temp = 1;
increment = true;
}
++temp;
} else if ((chs[i] - 1 == chs[i + 1])) {
// 在指定范围内按照ascii码值递减顺序来判断字符连续
if (increment) {
temp = 1;
increment = false;
}
++temp;
} else {
temp = 1;// 将临时计数变量置为1
}
} else {
temp = 1;
continue;
}
if (max < temp) {
max = temp;
}
}
int serialValue = pwdRulePo.getSerialnum() * password.length() / 100;
if(pwdRulePo.getSerialnum() * password.length() % 100 > 0){
serialValue++;
}
if (max >= serialValue) {
Map<String, Object> map = new HashMap<>(4);
map.put("serialnum", pwdRulePo.getSerialnum());
Result<Void> result = Result.create(IdmError.password_serial_illegality);
result.setMessage(TemplateUtil.buildTemplate(result.getMessage(), map));
result.setDesc(result.getMessage());
return result;
}
return Result.create();
}
/**
* 密码重复字符验证
*
* @param pwdRulePo
* @param password
* @return
*/
private Result<Void> isRepeatnum(PwdRulePo pwdRulePo, String password) {
char[] chs = password.toCharArray();
Map<Character, Integer> map = new HashMap<>();
for (int i = 0; i < chs.length; i++) {
Integer count = map.get(chs[i]);
if (count == null) {
count = 0;
}
count = count + 1;
map.put(chs[i], count);
}
// 最大重复数量
int maxNum = 0;
for (int num : map.values()){
if (num > maxNum) {
maxNum = num;
}
}
// 允许重复数量
int repeatValue = pwdRulePo.getRepeatnum() * password.length() / 100;
if (pwdRulePo.getRepeatnum() * password.length() % 100 > 0) {
repeatValue++;
}
if (maxNum >= repeatValue) {
Map<String, Object> temp = new HashMap<>(4);
temp.put("repeatnum", pwdRulePo.getRepeatnum());
Result<Void> result = Result.create(IdmError.password_repeat_illegality);
result.setMessage(TemplateUtil.buildTemplate(result.getMessage(), temp));
result.setDesc(result.getMessage());
return result;
}
return Result.create();
}
/**
* 组装必须包含字符的提示信息
*
* @param pwdRulePo 密码策略
* @return 提示信息
*/
private String mustMessage(PwdRulePo pwdRulePo) {
// 必须包含的类型
Integer mustValue = pwdRulePo.getMust();
// 必须有的类型
Integer ismustVlaue = pwdRulePo.getIsmust();
// 几种类型以上
Integer pwdkindnumVlaue = pwdRulePo.getPwdkindnum();
List<String> mustlist = new ArrayList<>();
Integer mustkind = 0;
if((mustValue & PwdRulePo.MUST_DIGIT) == PwdRulePo.MUST_DIGIT){
mustlist.add("数字");
mustkind++;
}
if((mustValue & PwdRulePo.MUST_LETTERCASE) == PwdRulePo.MUST_LETTERCASE){
mustlist.add("字母");
mustkind++;
} else {
if((mustValue & PwdRulePo.MUST_LOWERCASE) == PwdRulePo.MUST_LOWERCASE){
mustlist.add("小写字母");
mustkind++;
}
if((mustValue & PwdRulePo.MUST_UPPERCASE) == PwdRulePo.MUST_UPPERCASE){
mustlist.add("大写字母");
mustkind++;
}
}
if((mustValue & PwdRulePo.MUST_SPECIAL) == PwdRulePo.MUST_SPECIAL){
mustlist.add("特殊字符");
mustkind++;
}
Map<String, Object> map = new HashMap<>(4);
// 将提示信息加入到消息模板中
map.put("mustlist",mustlist);
// 将类型种类加入到消息模板中
map.put("pwdkind",pwdkindnumVlaue);
// 必须包含的字符
// 必须包含的值为0 或者 已经设置的字符类型数量和必须包含的种类数量相同(比如只设置数值,种类为1种以上, 只设置数值,字母, 种类2种以上)
List<String> ismustlist = new ArrayList<>();
if(IdmConstant.FALSE.equals(ismustVlaue) || pwdkindnumVlaue.equals(mustkind)){
ismustlist = null;
} else {
if((ismustVlaue & PwdRulePo.MUST_DIGIT) == PwdRulePo.MUST_DIGIT){
ismustlist.add("数字");
}
if((ismustVlaue & PwdRulePo.MUST_LETTERCASE) == PwdRulePo.MUST_LETTERCASE){
ismustlist.add("字母");
} else {
if((ismustVlaue & PwdRulePo.MUST_LOWERCASE) == PwdRulePo.MUST_LOWERCASE){
ismustlist.add("小写字母");
}
if((ismustVlaue & PwdRulePo.MUST_UPPERCASE) == PwdRulePo.MUST_UPPERCASE){
ismustlist.add("大写字母");
}
}
if((ismustVlaue & PwdRulePo.MUST_SPECIAL) == PwdRulePo.MUST_SPECIAL){
ismustlist.add("特殊字符");
}
}
// 将必须包含信息加入到消息模板中
map.put("ismustlist",ismustlist);
// 暂时使用错误码中的配置
return TemplateUtil.buildTemplate(IdmError.password_must_illegality.getMessage(), map);
}
/**
* 根据密码策略生成符合该密码策略规则的随机密码
* @param model 密码策略
* @return
*/
public String generatePwd(PwdRulePo model) {
StringBuffer passwords = new StringBuffer(); // 随机生成的密码
String digits = RandomStringUtils.random(1,DIGHT);;// 数字,去除(0、1)两个数字防止误读
String lettercases = RandomStringUtils.random(1, LETTER);// 字母包含 大小写字符(去掉了如:o l等易混淆字符)
String lowercases = RandomStringUtils.random(1, LOWWER);// 小写字符 大小写字母表去掉了如:o l等易混淆字符
String uppercases = RandomStringUtils.random(1,UPPER);// 大写字母
String specials = RandomStringUtils.random(1,ch);// 特殊字符
//如果密码策略为空时,则生成随机八位密码
if(null==model){
return passwords.append(digits).append(lowercases).append(uppercases).append(specials).append(digits).append(lowercases).append(lowercases).append(specials).toString();
}
Integer minLength=model.getLengthmin();
// Integer maxLength=model.getLengthmax();
Integer mustValue= model.getMust();
if(null==minLength){
minLength=8;
}
// if(null==maxLength){
// maxLength=20;
// }
if(null==mustValue){
mustValue=3;
}
Integer temp=minLength;//临时变量
Integer lenCount=0;//记录密码的实时长度
if((mustValue & PwdRulePo.MUST_DIGIT)==PwdRulePo.MUST_DIGIT){
passwords.append(digits);//加入数字
lenCount++;
}
//逻辑上字母常量与大小写字母常量不会同时出现
if((mustValue & PwdRulePo.MUST_LETTERCASE)==PwdRulePo.MUST_LETTERCASE){
passwords.append(lettercases);//加入字母
lenCount++;
} else {
if((mustValue & PwdRulePo.MUST_LOWERCASE) == PwdRulePo.MUST_LOWERCASE){
passwords.append(lowercases);//加入小写字符
lenCount++;
}
if((mustValue & PwdRulePo.MUST_UPPERCASE) == PwdRulePo.MUST_UPPERCASE){
passwords.append(uppercases);//加入大写字符
lenCount++;
}
}
if((mustValue & PwdRulePo.MUST_SPECIAL) == PwdRulePo.MUST_SPECIAL){
passwords.append(specials);//加入特殊字符
lenCount++;
}
passwords.append(RandomStringUtils.random(temp-lenCount,LOWWER));
return passwords.toString();
}
/**
* 获取密码评分
* 密码位数<6个字符,统一定性为“极弱”。
* <pre>
* 1、评分规则
* 密码长度
* 5 分: 小于等于 6 个字符
15 分: 7 到 9 个字符
20 分: 大于等于 10 个字符
字母
0 分: 没有字母
10 分: 全都是小(大)写字母
25 分: 大小写混合字母
数字
0 分: 没有数字
10 分: 1到2个数字
15 分: 大于等于 3 个数字
符号
0 分: 没有符号
15 分: 1 个符号
25 分: 大于 1 个符号
奖励
5分:字母+数字
10:字母+符号;数字+符号
15分:字母+数字+符号
2、最后的评分标准
>= 75: 强
>= 55: 中
>= 30: 弱
<30:极弱
</pre>
* @param pwd
* @return
*/
public int getPwdScore(String pwd) {
int score = 0;
if (org.apache.commons.lang3.StringUtils.isNotBlank(pwd)) {
// 长度
int length = pwd.length();
if (length < 6) {
return 5;
}
if (length <= 6) {
score += 5;
} else if (length <= 9) {
score += 15;
} else {
score += 20;
}
// 小写字母 大写字母 数字 其他字符数量
int lowercaseNum = 0;
int uppercaseNum = 0;
int digitalNum = 0;
int otherNum = 0;
for (int i = 0; i < length; i++) {
char c = pwd.charAt(i);
if (c <= '9' && c >= '0') {
digitalNum++;
} else if (c <= 'z' && c >= 'a') {
lowercaseNum++;
} else if (c <= 'Z' && c >= 'A') {
uppercaseNum++;
} else {
otherNum++;
}
}
// 字母
if (lowercaseNum > 0 && uppercaseNum > 0) {
score += 25;
} else if ((lowercaseNum + uppercaseNum) > 0) {
score += 10;
}
// 数字
if (digitalNum > 0 && digitalNum < 3) {
score += 10;
} else if (digitalNum >= 3) {
score += 15;
}
// 特殊字符
if (otherNum == 1) {
score += 15;
} else if (otherNum > 1) {
score += 25;
}
// 奖励
if ((lowercaseNum + uppercaseNum) > 0 && digitalNum > 0
&& otherNum > 0) {
score += 15;
} else if (otherNum > 0
&& (((lowercaseNum + uppercaseNum) > 0 || digitalNum > 0))) {
score += 10;
} else if ((lowercaseNum + uppercaseNum) > 0 && digitalNum > 0) {
score += 5;
}
}
return score;
}
}java
package com.commnetsoft.idm.util.am;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.data.model.Data;
import com.commnetsoft.idm.constant.AccountAuthLevel;
import com.commnetsoft.idm.constants.AccountProperty;
import com.commnetsoft.idm.core.exception.RSADecodeException;
import com.commnetsoft.idm.service.am.AccountService;
import com.commnetsoft.idm.util.RSAUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserUtil {
private static final Logger logger = LoggerFactory.getLogger(UserUtil.class);
@Autowired
private RSAUtil rsaUtil;
@Autowired
private AccountService accountService;
/**
* <pre>
* RSA解密,使用当前系统默认的公钥和私钥对字符串进行解密。
* 根据系统配置,是否启用密码加密传输来确定是否需要对密码进行解密。
* 如果:未开启密码加密传输则直接返回原数据。
* 否则返回密码解密之后的明文数据。
* </pre>
* @param str 明文数据 或者 密码 加密数据
* @return
* @throws Exception
*/
public String rsaDecode(String str) throws RSADecodeException {
if (StringUtils.isBlank(str)){
logger.error("UserUtil.rsaDecode,RSA解密字符串,字符串为空");
return null;
}
try {
str = rsaUtil.decryptBase64(str);
} catch (Exception e) {
throw new RSADecodeException(String.format("数据%s解密异常", str),e);
}
return str;
}
/**
* 根据账户模型,返回展示名称(脱敏)获取脱敏手机号或姓名(姓名优先)
* @param accountdata
* @return
*/
public String dealShowname(Data accountdata){
if (null == accountdata){
return null;
}
if(isauth(accountdata) && StringUtils.isNotBlank(accountdata.getString(AccountProperty.username))){
return dealUsername(accountdata.getString(AccountProperty.username));
} else if (StringUtils.isNotBlank(accountdata.getString(AccountProperty.mobilephone))){
return dealMobile(accountdata.getString(AccountProperty.mobilephone));
} else if (StringUtils.isNotBlank(accountdata.getString(AccountProperty.loginname))){
return dealLoginname(accountdata.getString(AccountProperty.loginname));
}
return null;
}
/**
* 登录名脱敏处理
* @return
*/
public static String dealLoginname(String loginname){
if(StringUtils.isBlank(loginname)){
return "";
}
StringBuffer result = new StringBuffer();
int length = loginname.length();
for (int i =0;i<length;i++) {
if(i == 0 || i== length-1){
result.append(loginname.charAt(i));
}else{
result.append('*');
}
}
return result.toString();
}
/**
* 手机号码脱敏处理
* @param mobilePhone
* @return
*/
public static String dealMobile(String mobilePhone){
if(StringUtils.isBlank(mobilePhone)){
return "";
}
StringBuffer result = new StringBuffer();
if(mobilePhone.length() > 7){
result.append(mobilePhone.substring(0, 3));
result.append("****");
result.append(mobilePhone.substring(7));
}
return result.toString();
}
/**
* 用户姓名脱敏处理
* @param username
* @return
*/
public static String dealUsername(String username){
if(StringUtils.isBlank(username)){
return "";
}
int length = username.length();
if(length == 1){
return username;
}
StringBuffer result = new StringBuffer();
if(length == 2){
return result.append("*").append(username.substring(1)).toString();
}else{
return result.append(username.substring(0, 1)).append("*").append(username.substring(length-1)).toString();
}
}
/**
* 身份证号 脱敏处理
* @param idcard
* @return
*/
public static String dealIdcard(String idcard){
if(StringUtils.isBlank(idcard)){
return "";
}
StringBuffer result = new StringBuffer();
if(idcard.length() >= 15){
result.append(idcard.substring(0, 1));
result.append("****************");
result.append(idcard.substring(idcard.length()-1));
}
return result.toString();
}
/**
* 邮箱脱敏处理
* @param email
* @return
*/
public static String dealEmail(String email){
if(StringUtils.isBlank(email)){
return "";
}
String[] str = StringUtils.split(email, "@");
if(str.length != 2){
return "";
}
String loginname = str[0];
StringBuffer result = new StringBuffer();
int length = loginname.length();
for (int i =0;i<length;i++) {
if(i == 0 || i== length){
result.append(loginname.charAt(i));
}else{
if(length>3 && (i ==1 || i == length-1)){
result.append(loginname.charAt(i));
}else{
result.append('*');
}
}
}
result.append("@").append(str[1]);
return result.toString();
}
/**
* 判断用户是否为实名认证用户。 如果true表示实名认证 如果false 表示未实名认证
* @param accountdata
* @return
*/
public static boolean isauth(Data accountdata){
if (null == accountdata){
return false;
}
if (null == accountdata.getInteger(AccountProperty.accountstatus)){
return false;
}
Integer accountstatus = accountdata.getInteger(AccountProperty.accountstatus);
if(AccountAuthLevel.noauth.getValue().equals(accountstatus) || AccountAuthLevel.lowauth.getValue().equals(accountstatus)){
return false;
}
return true;
}
}java
package com.commnetsoft.idm.util.safety;
import com.commnetsoft.core.cache.CacheDuration;
import com.commnetsoft.idm.model.safety.ValiCodePo;
import org.apache.commons.lang.RandomStringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
@Service
@RefreshScope
public class IdmCodeCacheUtil {
@Value("#{idmConstantConfig.valicode_errnum}")
private Integer valicode_errnum;
@Value("#{idmConstantConfig.valicode_time}")
private Integer valicode_time;
/**
* 生成验证码
* @param number
* @return
*/
@CachePut(value = "valicode",key = "#model+#number")
@CacheDuration(var="#{idmConstantConfig.MILLIS_PER_MINUTE() * (idmConstantConfig.valicode_time + 15)}")//缓存时间加15分钟,为了页面接口提示验证码超时
public ValiCodePo generateValicode(String model, String number, String ip){
ValiCodePo c = new ValiCodePo();
//生成五位手机验证码
c.setCode(RandomStringUtils.randomNumeric(5));
c.setIp(ip);
//设置验证码有效期.
c.setExpiredtime((long)(valicode_time)*60*1000+System.currentTimeMillis());
return c;
}
/**
* 获取验证码
* @param number
* @return
*/
@Cacheable(value = "valicode",key = "#model+#number")
@CacheDuration(var="#{idmConstantConfig.MILLIS_PER_MINUTE() * (idmConstantConfig.valicode_time + 15)}")//缓存时间加15分钟,为了页面接口提示验证码超时
public ValiCodePo getValicode(String model, String number, String ip){
//未在缓存中获取到,说明未生成过验证码
return null;
}
/**
* 清空验证码
* @param number
* @return
*/
@CacheEvict(value = "valicode",key = "#model+#number")
public void resetCode(String model, String number){
}
/**
* 增加一次验证码错误次数 +1
* @param model
* @param number
* @param valiCode
* @return
*/
@CachePut(value = "valicode",key = "#model+#number")
@CacheDuration(var="#{idmConstantConfig.MILLIS_PER_MINUTE() * (idmConstantConfig.valicode_time + 15)}")//缓存时间加15分钟,为了页面接口提示验证码超时
public ValiCodePo addValicodenum(String model, String number, ValiCodePo valiCode){
if(null == valiCode){return null;}
valiCode.setNum(valiCode.getNum()+1);
return valiCode;
}
}java
package com.commnetsoft.idm.util.safety;
import com.commnetsoft.commons.Result;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.idm.IdmError;
import com.commnetsoft.idm.model.safety.ValiCodePo;
import com.commnetsoft.idm.service.sys.MsgTemplateService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
/**
* 短信/邮箱验证码工具类
*/
@Service
public class IdmValiCodeUtil {
private static final Logger logger = LoggerFactory.getLogger(IdmValiCodeUtil.class);
@Autowired
private IdmCodeCacheUtil idmcodeCacheUtil;
@Autowired
private MsgTemplateService msgTemplateService;
@Value("#{idmConstantConfig.valicode_errnum}")
private Integer valicode_errnum;
/**
* 生成手机号验证码,并发送验证码
* @param model
* @param mobilephone
* @param userip
* @return
*/
public Result<Void> sendSmsCode(String model, String mobilephone, String userip, Integer msgtemplateid){
//获取5位数字验证码
ValiCodePo c = idmcodeCacheUtil.generateValicode(model, mobilephone, userip);
String code = c.getCode();
//TODO 手机号发送手机号限制.
if(!IdmValidateUtil.valMobilePhone(mobilephone)){
}
//验证码发送
Map<String, Object> map = new HashMap<String, Object>();
map.put("verifycode", code);
String content = msgTemplateService.buildTemplate(msgtemplateid,map);
if (StringUtils.isBlank(content)){
logger.error("短信模板生成失败,模板id={},mobilephone={},code={}",msgtemplateid,mobilephone,code);
return Result.create(IdmError.msgtemplate_error);
}
//TODO 短信发送实现类
System.out.println(content);
logger.error("发送验证码失败,mobilephone={},content={}",mobilephone,content);
return Result.create();
}
/**
* 生成邮箱验证码,并发送验证码
* @param model
* @param email
* @param userip
* @return
*/
public Result<Void> sendEmailCode(String model, String email, String userip, Integer msgtemplateid){
//获取5位数字验证码
ValiCodePo c = idmcodeCacheUtil.generateValicode(model, email, userip);
String code = c.getCode();
//TODO 邮箱发送验证码限制.
//验证码发送
Map<String, Object> map = new HashMap<String, Object>();
map.put("verifycode", code);
String content = msgTemplateService.buildTemplate(msgtemplateid,map);
if (StringUtils.isBlank(content)){
logger.error("邮箱模板生成失败,模板id={},email={},code={}",msgtemplateid,email,code);
return Result.create(IdmError.msgtemplate_error);
}
//TODO 邮箱发送验证码实现类
System.out.println(content);
logger.error("发送验证码失败,email={},content={}",email,content);
return Result.create();
}
/**
* 校验相关验证码
* @param number
* @param code
* @return
*/
public Result<Void> valiCode(String model, String number, String code, String ip){
ValiCodePo valicode = idmcodeCacheUtil.getValicode(model,number,ip);
if(null == valicode){
return Result.create(IdmError.code_valifail);
}
//验证是否次数失效
int num = valicode.getNum();
if (num > 0 && num >= valicode_errnum){
logger.info("验证码输入错误次数过多,失效,【number={},code={},model={},ip={}】,错误次数={}",number,code,model,ip,num);
return Result.create(IdmError.code_expire);
}
//验证是否失效
if(valicode.getExpiredtime()<System.currentTimeMillis()){
logger.info("验证码超时失效,【number={},code={},model={},ip={}】,当前时间={},失效时间={}",number,code,model,ip,System.currentTimeMillis(),valicode.getExpiredtime());
return Result.create(IdmError.code_expire);
}
//验证验证码
if(false == StringUtils.equals(code, valicode.getCode())){
idmcodeCacheUtil.addValicodenum(model,number,valicode);
logger.info("验证码验证失效,【number={},code={},model={},ip={}】,正确的code={}",number,code,model,ip,valicode.getCode());
return Result.create(IdmError.code_valifail);
}
return Result.create();
}
/**
* 清空相关验证码
* @param model
* @param number
* @return
*/
public void cleanCode(String model, String number){
idmcodeCacheUtil.resetCode(model, number);
}
}java
package com.commnetsoft.idm.util.safety;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.commons.utils.ValidateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 对象字段合法性验证类
*/
@Service
@RefreshScope
public class IdmValidateUtil extends ValidateUtils {
public final static Logger logger = LoggerFactory.getLogger(IdmValidateUtil.class);
@Value("#{idmConstantConfig.loginname_regexp}")
private String loginname_regexp;
/**
* 登录名校验。
* @param loginname
* @return
*/
public boolean valLoginname(String loginname) {
if(StringUtils.isBlank(loginname)){
return false;
}
String check = loginname_regexp;
if(StringUtils.isBlank(check)){
check = "^([a-zA-Z1-9]+)$";
}else{
//去除前后反编译符号
if(check.startsWith("/")){
check = check.substring(1, check.length()-1);
}
if(check.endsWith("/")){
check = check.substring(check.length()-1, check.length());
}
}
Pattern regex = Pattern.compile(check);
Matcher matcher = regex.matcher(loginname);
boolean isMatched = matcher.matches();
return isMatched;
}
}java
package com.commnetsoft.idm.util.safety;
import com.commnetsoft.commons.utils.DateUtils;
import com.commnetsoft.core.cache.CacheDuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
/**
* @ClassName JsSdkCodeCache
* @Author wyj
* @Date 2020/9/25 0025 14:03
* @Version 1.0
*/
@Service
public class JsSdkCodeCache {
public final static Logger logger = LoggerFactory.getLogger(JsSdkCodeCache.class);
@CachePut(value = "jssdk_code",key = "#servicecode + #userip")
@CacheDuration(DateUtils.MILLIS_PER_MINUTE*5)
public String putCode(String servicecode, String userip, String jssdkcode){
return jssdkcode;
}
@Cacheable(value = "jssdk_code",key = "#servicecode + #userip")
@CacheDuration(DateUtils.MILLIS_PER_MINUTE*5)
public String getCode(String servicecode,String userip){
return null;
}
/**
* 清空jssdk校验码
* @param servicecode
* @return
*/
@CacheEvict(value = "jssdk_code",key = "#servicecode + #userip")
public void resetCode(String servicecode,String userip){
}
}java
package com.commnetsoft.idm.util.safety;
import com.commnetsoft.commons.Result;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.idm.IdmError;
import com.commnetsoft.idm.util.UUIDGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.validation.constraints.NotNull;
/**
* JsSdk初始化认证工具类
* @ClassName JsSdkCodeUtil
* @Author wyj
* @Date 2020/9/25 0025 14:04
* @Version 1.0
*/
@Service
public class JsSdkCodeUtil {
private static final Logger logger = LoggerFactory.getLogger(JsSdkCodeUtil.class);
public transient final static String JSSDK_NAMESPACE = "idm_jssdk";
@Autowired
private JsSdkCodeCache jsSdkCodeCache;
/**
* 根据开放平台servicecode生成相关jssdk验签
* @param servicecode 开放平台code
* @return
*/
public String createCode(String servicecode, String userip){
if (StringUtils.isBlank(servicecode) || StringUtils.isBlank(userip)){
return null;
}
//创建code授权码
String code = UUIDGenerator.getInstance().generate() + "-" + JSSDK_NAMESPACE;
jsSdkCodeCache.putCode(servicecode,userip,code);
return code;
}
/**
* 验证jssdk验签,若验证成功,则直接清理缓存
* @param servicecode 开放平台code
* @param signcode jssdk分发的code
* @return
*/
public Result<Void> valisdksign(@NotNull String servicecode, @NotNull String userip, @NotNull String signcode){
String signcodeCache = jsSdkCodeCache.getCode(servicecode,userip);
if (!StringUtils.equals(signcode,signcodeCache)){
return Result.create(IdmError.fail);
}
//认证成功,清理验证code
jsSdkCodeCache.resetCode(servicecode,userip);
return Result.create();
}
}java
package com.commnetsoft.idm.util.safety;
import com.commnetsoft.commons.utils.DateUtils;
import com.commnetsoft.core.cache.CacheDuration;
import com.commnetsoft.idm.model.am.StepPo;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class StepCacheUtil {
/**
* 获取步骤码时,step可以为null
* @param model
* @param serialnum
* @return
*/
@Cacheable(value = "step_data",key = "#model+#serialnum")
@CacheDuration(DateUtils.MILLIS_PER_MINUTE*15)
public StepPo generateStepData(String model, String serialnum, StepPo step){
return step;
}
/**
* 修改步骤码
* @param model
* @param serialnum
* @return
*/
@CachePut(value = "step_data",key = "#model+#serialnum")
@CacheDuration(DateUtils.MILLIS_PER_MINUTE*15)
public StepPo updateStepData(String model, String serialnum, StepPo step){
return step;
}
/**
* 销毁缓存
*/
@CacheEvict(value = "step_data",key = "#model+#serialnum")
public void destroyCode(String model,String serialnum){
//ignore
}
}java
package com.commnetsoft.idm.util.safety;
import com.commnetsoft.commons.Result;
import com.commnetsoft.commons.utils.DateUtils;
import com.commnetsoft.core.utils.SpringContextUtil;
import com.commnetsoft.idm.IdmError;
import com.commnetsoft.idm.model.am.StepPo;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 步骤流程安全工具类.多例模式
* @version 1.0
*/
public class StepUtil {
public transient final static String MODELNAME_RESETPWD = "resetpwd";
public transient final static String MODELNAME_BINDMAIL = "bindmail";
public transient final static String MODELNAME_REBINDMOBILE = "rebindmobile";
public transient final static String MODELNAME_UPLEVEL = "uplevel";
public transient final static String MODELNAME_REGSAVE = "regsave";
public transient final static String MODELNAME_GETMOBILE = "getmobile";
public transient final static String MODELNAME_EDITPWD = "editpwd"; //修改密码
public transient final static String MODELNAME_APPLOGIN = "applogin"; //app登录
public transient final static String MODELNAME_CLOSEUSER = "closeuser"; //销户
public transient final static String MODELNAME_ACTIVEUSER = "activeuser"; //账号激活
/**
* 步骤缓存
*/
private final static String NAMESPACE = "step";
private final static Integer ONE = 1;
private static StepCacheUtil stepCacheUtil = SpringContextUtil.getBean(StepCacheUtil.class);
private static Map<String,StepUtil> instancemap = new HashMap<String, StepUtil>();
private StepUtil() {
}
/**
* 多例模式。获取认证对象
* @param modelname 根据名称获取实例对象
* @return
*/
public static StepUtil getInstance(String modelname){
StepUtil util = instancemap.get(modelname);
if(util == null){
util = new StepUtil();
instancemap.put(modelname, util);
}
return util;
}
/**
* <p>
* 开始流程。需要设置总的步骤数。流程需要在15分钟之内走完否则将失效。 失效建议重新开始流程.
* 成功返回步骤码。失败返回null
* 返回值在后面流程中使用
* </p>
* @param totalStep 总的步骤数。必须大于1
* @return
*/
public String begin(Integer totalStep){
if(totalStep == null || totalStep <2){
return null;
}
StepPo step = new StepPo();
step.setCurstep(ONE);
step.setTotalstep(totalStep);
String key = DateUtils.parseDateToString(new Date(System.currentTimeMillis()),DateUtils.TIMESTAMP_PATTERN) +RandomStringUtils.randomNumeric(4);
stepCacheUtil.generateStepData(NAMESPACE, key, step);
return key;
}
/**
* <p>
* 结束流程。
* </p>
* @param stepKey 步骤唯一码
*/
public void end(String stepKey){
stepCacheUtil.destroyCode(NAMESPACE, stepKey);
}
/**
* <p>
* 验证当前是否为有效步骤。步骤只能按部就班,所以当前步骤为5,如果传5或者小于5则验证通过(可能返回到上一步操作)。否则验证不通过。
* 验证通过自动记录下一步骤.
* <b>注意vali方法基本上从2开始</b>
* 错误有:步骤唯一码无效或者已失效。验证失败,非法操作
* </p>
* @param stepKey 步骤唯一码
* @param curstep 验证步骤,当前的步骤码
* @return
*/
public Result<Void> vali(String stepKey, Integer curstep){
if(StringUtils.isBlank(stepKey) || curstep == null){
return Result.create(IdmError.step_key_expire);
}
StepPo s = stepCacheUtil.generateStepData(NAMESPACE, stepKey, null);
if(s == null){
return Result.create(IdmError.step_key_expire);
}
Integer step = s.getCurstep();
if( (step+1) < curstep){
return Result.create(IdmError.step_vali_fail);
}
//验证通过
return Result.create();
}
/**
* <p>
* 验证当前是否为有效步骤。步骤只能按部就班,所以当前步骤为5,如果传5或者小于5则验证通过(可能返回到上一步操作)。否则验证不通过。
* 验证通过自动记录下一步骤.如果步骤完成则将标记步骤完成,不能再次使用
* 错误有:步骤唯一码无效或者已失效。验证失败,非法操作
* </p>
* @param stepKey 步骤唯一码
* @param curstep 验证步骤,当前的步骤码
* @return
*/
public Result<Void> next(String stepKey, Integer curstep){
if(StringUtils.isBlank(stepKey) || curstep == null){
return Result.create(IdmError.step_key_expire);
}
StepPo s = stepCacheUtil.generateStepData(NAMESPACE, stepKey, null);
if(s == null){
return Result.create(IdmError.step_key_expire);
}
Integer step = s.getCurstep();
if( (step+1) < curstep){
return Result.create(IdmError.step_vali_fail);
}
//验证通过
if(curstep.equals(step+1)){
if(!curstep.equals(s.getTotalstep())){
s.setCurstep(curstep);
stepCacheUtil.updateStepData(NAMESPACE, stepKey, s);
}
}
return Result.create();
}
/**
* 存放临时数据
* @param stepKey 步骤确认码
* @param key 临时数据key
* @param value 临时数据value
*/
public <T extends Serializable> boolean put(String stepKey, String key, T value){
return putValue(stepKey, key, value);
}
/**
* 获取存放的临时数据的值
* @param stepKey 步骤确认码
* @param key 临时数据key
* @return
*/
public String getStringValue(String stepKey, String key){
Object o = getValue(stepKey, key);
if(o instanceof String){
return (String) o;
}
return null;
}
/**
* 获取存放的临时数据的值
* @param stepKey 步骤确认码
* @param key 临时数据key
* @return
*/
public Integer getIntValue(String stepKey, String key){
Object o = getValue(stepKey, key);
if(o instanceof Integer){
return (Integer) o;
}
return null;
}
/**
* 获取存放的临时数据的值
* @param stepKey 步骤确认码
* @param key 临时数据key
* @return
*/
public <T> T get(String stepKey, String key){
Object o = getValue(stepKey, key);
if(o == null){
return null;
}
return (T)o;
}
/**
* 存放临时数据
* @param stepKey 步骤确认码
* @param key 临时数据key
* @param value 临时数据value
*/
private boolean putValue(String stepKey, String key, Object value){
if(StringUtils.isBlank(stepKey) || StringUtils.isBlank(key) || value == null){
return false;
}
StepPo s = stepCacheUtil.generateStepData(NAMESPACE, stepKey, null);
if(s == null){
return false;
}
Map<String, Object> map = s.getTemp();
if(map == null){
map = new HashMap<String, Object>();
s.setTemp(map);
}
map.put(key, value);
stepCacheUtil.updateStepData(NAMESPACE, stepKey, s);
return true;
}
/**
* 获取临时数据
* @param stepKey 步骤确认码
* @param key 临时数据key
* @return
*/
public <T> T getValue(String stepKey, String key){
if(StringUtils.isBlank(stepKey) || StringUtils.isBlank(key)){
return null;
}
StepPo s = stepCacheUtil.generateStepData(NAMESPACE, stepKey, null);
if(s == null){
return null;
}
Map<String, Object> map = s.getTemp();
if(map == null){
return null;
}
Object o = map.get(key);
if(o == null){
return null;
}
return (T)o;
}
}java
package com.commnetsoft.idm.util.safety;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.Random;
/**
* 生成验证码工具类.
* @ClassName VerifyImage
* @Author wyj
* @Date 2020/8/11 0011 10:34
* @Version 1.0
*/
public class VerifyImage {
public String sRand = "";
/**
* 给定范围获得随机颜色
* @param fc
* @param bc
* @return
*/
public Color getRandColor(int fc, int bc) {
Random random = new Random();
if (fc > 255) fc = 255;
if (bc > 255) bc = 255;
int r = fc + random.nextInt(bc - fc);
int g = fc + random.nextInt(bc - fc);
int b = fc + random.nextInt(bc - fc);
return new Color(r, g, b);
}
/**
* 生成验证码图片
* @return
*/
public BufferedImage creatImage() {
// 在内存中创建图象
int width = 160, height = 60;
BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
// 获取图形上下文
Graphics g = image.getGraphics();
// 生成随机类
Random random = new Random();
// 设定背景色
g.setColor(getRandColor(200, 250));
g.fillRect(0, 0, width, height);
// 设定字体
g.setFont(new Font("Times New Roman", Font.PLAIN, 60));
// 画边框
// g.setColor(new Color());
// g.drawRect(0,0,width-1,height-1);
// 随机产生155条干扰线,使图象中的认证码不易被其它程序探测到
g.setColor(getRandColor(160, 200));
for (int i = 0; i < 155; i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
g.drawLine(x, y, x + xl, y + yl);
}
// 取随机产生的认证码(4位数字)
// String rand = request.getParameter("rand");
// rand = rand.substring(0,rand.indexOf("."));
for (int i = 0; i < 4; i++) {
String rand = String.valueOf(random.nextInt(10));
sRand += rand;
// 将认证码显示到图象中
// 调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成
g.setColor(new Color(20 + random.nextInt(110), 20 + random
.nextInt(110), 20 + random.nextInt(110)));
g.drawString(rand, 30 * i + 20, 50);
}
// 图象生效
g.dispose();
return image;
}
/**
* 获取随机码
* @return
*/
public String getRandCode() {
return sRand;
}
public void setRandCode(String rand) {
sRand = rand;
}
}java
package com.commnetsoft.idm.util.safety;
import com.commnetsoft.commons.utils.DateUtils;
import com.commnetsoft.core.cache.CacheDuration;
import com.commnetsoft.idm.model.am.VerifyImageCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
/**
* 图片验证码缓存
* @ClassName VerifyImageCacheUtil
* @Author wyj
* @Date 2020/8/11 0011 16:32
* @Version 1.0
*/
@Service
public class VerifyImageCacheUtil {
private static final Logger logger = LoggerFactory.getLogger(VerifyImageCacheUtil.class);
/**
* 图片验证码缓存
* @param verifyImageCache
* @param verifyUid
* @return
*/
@CachePut(value = "verifyImg",key = "#verifyUid+'-'+#verifyModel")
@CacheDuration(DateUtils.MILLIS_PER_MINUTE*5)
public VerifyImageCache putVerifyRandCode (VerifyImageCache verifyImageCache, String verifyUid, String verifyModel){
if (null == verifyImageCache){
return null;
}
return verifyImageCache;
}
/**
* 获取图片验证码对象
* @param verifyUid
* @return
*/
@Cacheable(value = "verifyImg",key = "#verifyUid+'-'+#verifyModel")
@CacheDuration(DateUtils.MILLIS_PER_MINUTE*5)
public VerifyImageCache getVerifyRandCode (String verifyUid, String verifyModel){
return null;
}
/**
* 清理图片验证码缓存
* @param verifyUid
* @param verifyModel
*/
@CacheEvict(value = "verifyImg",key = "#verifyUid+'-'+#verifyModel")
public void resetImageCache(String verifyUid, String verifyModel){
}
/**
* 根据登录ip设置登录错误次数缓存
* @param keystr
* @param errnum
* @return
*/
@CachePut(value = "loginErrnum",key = "#keystr")
@CacheDuration(DateUtils.MILLIS_PER_HOUR)
public Integer putloginerrnum(String keystr, Integer errnum){
return errnum;
}
/**
* 根据登录ip获取登录错误次数缓存
* @param keystr
* @return
*/
@Cacheable(value = "loginErrnum",key = "#keystr")
@CacheDuration(DateUtils.MILLIS_PER_HOUR)
public Integer getloginerrnum(String keystr){
return null;
}
}java
package com.commnetsoft.idm.util.safety;
import com.commnetsoft.commons.Result;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.commons.utils.UUIDUtils;
import com.commnetsoft.idm.IdmError;
import com.commnetsoft.idm.constants.IdmConstant;
import com.commnetsoft.idm.model.am.VerifyImageCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
/**
* @ClassName VerifyImageUtil
* @Author wyj
* @Date 2020/8/11 0011 17:21
* @Version 1.0
*/
@Service
@RefreshScope
public class VerifyImageUtil {
private static final Logger logger = LoggerFactory.getLogger(VerifyImageCacheUtil.class);
@Autowired
private VerifyImageCacheUtil verifyImageCacheUtil;
@Value("#{idmConstantConfig.sso_imgcode_enable}")
private Integer sso_imgcode_enable;
/**
* 图片验证码信息存入缓存
* @param verifyUid
* @param verfiyModel
* @param randCode
* @param userip
* @return
*/
public Result<Void> putVerifyImgCache(String verifyUid, Integer verfiyModel, String randCode, String userip){
if (StringUtils.isBlank(verifyUid) || null == verfiyModel || StringUtils.isBlank(randCode) || StringUtils.isBlank(userip)){
logger.error("将图片验证码存入缓存中,参数为空.");
return Result.create(IdmError.fail);
}
//校验唯一码UUID格式是否正确
if (!UUIDUtils.isUuid(verifyUid)){
logger.error("获取图片验证码错误,verifyUid不合规,verifyUid={}",verifyUid);
return Result.create(IdmError.fail);
}
VerifyImageCache verifyImageCache = new VerifyImageCache();
verifyImageCache.setVerifyUid(verifyUid);
verifyImageCache.setVerifyModel(verfiyModel);
verifyImageCache.setRandCode(randCode);
verifyImageCache.setUserip(userip);
verifyImageCacheUtil.putVerifyRandCode(verifyImageCache,verifyUid,verfiyModel+"");
return Result.create();
}
/**
* 校验图片验证码是否合法
* @param verifyUid
* @param verfiyModel
* @param verifycode
* @param userip
* @return
*/
public Result<Void> valiVerifyImg(String verifyUid, Integer verfiyModel, String verifycode, String userip){
if (StringUtils.isBlank(verifyUid) || null == verfiyModel || StringUtils.isBlank(verifycode) || StringUtils.isBlank(userip)){
return Result.create(IdmError.verifyImgCode_fail);
}
VerifyImageCache verifyImageCache = verifyImageCacheUtil.getVerifyRandCode(verifyUid, verfiyModel.toString());
if (null == verifyImageCache){
return Result.create(IdmError.verifyImgCode_past);
}
if (!StringUtils.equals(userip,verifyImageCache.getUserip())){
return Result.create(IdmError.verifyImgCode_past);
}
if (!StringUtils.equalsIgnoreCase(verifycode, verifyImageCache.getRandCode())){
return Result.create(IdmError.verifyImgCode_fail);
}
//图片验证码正确清理缓存
verifyImageCacheUtil.resetImageCache(verifyUid, verfiyModel.toString());
return Result.create();
}
/**
* 是否需要图片验证码。 根据IP源 来判断。当同一个IP源大于3次错误 则出现验证码
* @param ip ip地址
* @param model 模块
* @return
*/
public boolean isNeedImgcode(String ip, Integer model){
if (IdmConstant.FALSE.equals(sso_imgcode_enable)){
//未开启图片验证码,直接返回false
return false;
}
String key = buildKey(ip, model);
Integer num = verifyImageCacheUtil.getloginerrnum(key);
if (null == num){
num = IdmConstant.ZERO;
}
if(num > 3){
return true;
}
return false;
}
/**
* 增加错误次数,到达一定次数,需要登录验证码
* @param ip
* @param model
* @return
*/
public Integer addImgcodenum(String ip, Integer model){
if (IdmConstant.FALSE.equals(sso_imgcode_enable)){
//未开启图片验证码,直接返回false
return IdmConstant.ZERO;
}
String key = buildKey(ip, model);
Integer num = verifyImageCacheUtil.getloginerrnum(key);
if (null == num){
num = IdmConstant.ZERO;
}
//可以少更新一次缓存如果num>3的话
if(num > 3){
return num;
}
num = num+1;
verifyImageCacheUtil.putloginerrnum(key,num);
return num;
}
/**
* 生成换成中实际的key 存储为 key+"_"+model
* @param key
* @param model
* @return
*/
private String buildKey(String key, Integer model){
StringBuffer result = new StringBuffer(key);
return result.append(IdmConstant.SPLIT_UNDERLINE).append(model).toString();
}
}java
package com.commnetsoft.idm.util.sso;
import com.alibaba.fastjson.JSON;
import com.commnetsoft.commons.Result;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.idm.IdmError;
import com.commnetsoft.idm.constants.AccountProperty;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.MalformedClaimException;
import org.jose4j.jwt.NumericDate;
import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.lang.JoseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.validation.constraints.NotNull;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Map;
/**
* JWT工具类
*/
public class JwtUtil {
private static Logger logger = LoggerFactory.getLogger(JwtUtil.class);
/**
* jwt签发者
*/
public final static String JWT_ISS = "commnetsoftJWT";
/**
* 接收jwt的一方
*/
public final static String JWT_AUD_WEB = "commnetsoftWeb";//前端接收
/**
* 构建一个JWT Token
* @param jsonWebSignature jwt签署器
* @param id 账号id
* @param ip 登录客户端ip
* @param expiration jwt有效时长,单位分钟
* @return
* @throws Exception
*/
public static String generateJwt(JsonWebSignature jsonWebSignature, @NotNull String id, String ip, int expiration) throws JoseException {
JwtClaims jwtClaims = new JwtClaims();
jwtClaims.setIssuer(JWT_ISS);//jwt签发者
Map<String,Object> sub = new HashMap<String,Object>();
sub.put(AccountProperty.id,id);
if (StringUtils.isNotBlank(ip)){
sub.put("ip",ip);
}
jwtClaims.setSubject(JSON.toJSONString(sub));//主题
jwtClaims.setAudience(JWT_AUD_WEB);//接收jwt的一方
jwtClaims.setExpirationTimeMinutesInTheFuture(expiration);//到期时间(分钟)
jwtClaims.setNotBeforeMinutesInThePast(0);//在此之前不可用
jwtClaims.setIssuedAtToNow();//签发时间
jwtClaims.setGeneratedJwtId();//jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
jsonWebSignature.setPayload(jwtClaims.toJson());
String jwt = jsonWebSignature.getCompactSerialization();
return jwt;
}
/**
* 根据 Consumer还原 jwt 实体
*
* @param jwtConsumer
* @param jwt
* @return
* @throws InvalidJwtException
*/
public static JwtClaims consumerJwt(JwtConsumer jwtConsumer, String jwt) throws InvalidJwtException {
return jwtConsumer.processToClaims(jwt);
}
/**
* jwt是否未超时
* @param jwtClaims
* @return
* @throws MalformedClaimException
*/
public static Result<Void> isOvertimeJwt(JwtClaims jwtClaims) throws MalformedClaimException {
//到期时间是否在当前时间之前
if (jwtClaims.getExpirationTime().isBefore(NumericDate.now())){
return Result.create(IdmError.verify_token_overtime);
}
return Result.create();
}
/**
* 根据jwt获取其超时时间戳
* @param jwtClaims
* @return
* @throws MalformedClaimException
*/
public static Timestamp getExTimeByJwt(JwtClaims jwtClaims) throws MalformedClaimException{
return new Timestamp(jwtClaims.getExpirationTime().getValueInMillis());
}
/**
* 验证JWT是否合法(不对其过期做检测)
* @param jwtClaims
* @param ip
* @return
* @throws MalformedClaimException
*/
public static Result<Void> verifyJwtNoTime(JwtClaims jwtClaims, String ip) throws MalformedClaimException {
//检测jwt是否合法
if (!StringUtils.equals(JWT_ISS,jwtClaims.getIssuer())){
return Result.create(IdmError.verify_token_illegality);
}
//jwt多方提供时,需选择接收方常量
if (!StringUtils.equals(JWT_AUD_WEB,jwtClaims.getAudience().get(0))){
return Result.create(IdmError.verify_token_illegality);
}
Map<String,Object> sub = JSON.parseObject(jwtClaims.getSubject());
//ip不等则说明jwt被盗用
if (StringUtils.isNotBlank(ip) && !StringUtils.equals(ip,sub.get("ip").toString())){
return Result.create(IdmError.verify_token_illegality);
}
return Result.create();
}
/**
* 验证JWT是否有效(优先校验是否过期)
* @param jwtClaims jwt验签器
* @param ip
* @return
* @throws MalformedClaimException
*/
public static Result<Void> verifyJwt(JwtClaims jwtClaims, String ip) throws MalformedClaimException {
//到期时间是否在当前时间之前
Result<Void> isOvertime = isOvertimeJwt(jwtClaims);
if (!isOvertime.successful()){
return isOvertime;
}
return verifyJwtNoTime(jwtClaims, ip);
}
/**
* 根据jwt返回账户id(校验jwt可用性)
* @param jwtClaims
* @param ip
* @return
*/
public static Result<String> getAccountidByJwt(JwtClaims jwtClaims, String ip) throws MalformedClaimException{
Result<Void> verifyresult = verifyJwt(jwtClaims,ip);
if (!verifyresult.successful()){
return Result.create(verifyresult);
}
Map<String,Object> sub = JSON.parseObject(jwtClaims.getSubject());
String id = sub.get(AccountProperty.id).toString();
return Result.create(id);
}
/**
* 检测JWT是否在置换时间内
* @param jwtClaims
* @param replacement
* @return
* @throws MalformedClaimException
*/
public static Result<String> isinreplace(JwtClaims jwtClaims,int replacement) throws MalformedClaimException {
NumericDate replaceStartTime = jwtClaims.getExpirationTime();
replaceStartTime.addSeconds(-(replacement * 60)); //置换时间开始时间点
NumericDate replaceExpirationTime = jwtClaims.getExpirationTime();
replaceExpirationTime.addSeconds(replacement * 60);//置换时间结束时间点
if (NumericDate.now().isBefore(replaceStartTime)){
//当前时间在置换开始时间点之前,未到允许置换时间
return Result.create(IdmError.verify_token_notreplace);
}
if (NumericDate.now().isAfter(replaceExpirationTime)){
//当前时间在置换结束时间点之后,已过票据置换允许时间
return Result.create(IdmError.verify_token_overreplace);
}
//允许置换,则返回相关用户id
Map<String,Object> sub = JSON.parseObject(jwtClaims.getSubject());
String id = sub.get(AccountProperty.id).toString();
return Result.create(id);
}
}java
package com.commnetsoft.idm.util.sso;
import com.commnetsoft.core.cache.CacheDuration;
import com.commnetsoft.idm.model.sso.SsoCodeBo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
/**
* 第三方应用单点登录授权码Code值缓存
* @ClassName SsoCodeCache
* @Author wyj
* @Date 2020/9/3 0003 16:02
* @Version 1.0
*/
@Service
@RefreshScope
public class SsoCodeCache {
private static final Logger logger = LoggerFactory.getLogger(SsoCodeCache.class);
@Value("#{idmConstantConfig.ssocode_expiration}")
private Integer ssocode_expiration;
@CachePut(value = "sso_code",key = "#codeId")
@CacheDuration(var="#{idmConstantConfig.MILLIS_PER_MINUTE() * idmConstantConfig.ssocode_expiration}")
public SsoCodeBo putCode(String codeId, SsoCodeBo ssoCodeBo){
return ssoCodeBo;
}
@Cacheable(value = "sso_code",key = "#codeId")
@CacheDuration(var="#{idmConstantConfig.MILLIS_PER_MINUTE() * idmConstantConfig.ssocode_expiration}")
public SsoCodeBo getCode(String codeId){
return null;
}
/**
* 清空临时授权码
* @param codeId
* @return
*/
@CacheEvict(value = "sso_code",key = "#codeId")
public void resetCode(String codeId){
}
}java
package com.commnetsoft.idm.util.sso;
import com.commnetsoft.commons.Result;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.idm.IdmError;
import com.commnetsoft.idm.model.sso.SsoCodeBo;
import com.commnetsoft.idm.util.UUIDGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import javax.validation.constraints.NotNull;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* 第三方应用单点登录Code值授权码生成工具类
* @ClassName SsoCodeUtil
* @Author wyj
* @Date 2020/9/3 0003 16:02
* @Version 1.0
*/
@Service
@RefreshScope
public class SsoCodeUtil {
private static final Logger logger = LoggerFactory.getLogger(SsoCodeUtil.class);
@Autowired
private SsoCodeCache ssoCodeCache;
@Value("#{idmConstantConfig.ssocode_expiration}")
private Integer ssocode_expiration;
public boolean isSsoCode(String key){
if(StringUtils.isNotBlank(key) && key.endsWith(SsoCodeBo.SSO_CODE_NAMESPACE)){
return true;
}else{
return false;
}
}
/**
* 创建单点登录授权码(存入缓存)
* @param uid 账户id
* @param appid 应用id
* @param userip 用户ip
* @return
*/
public SsoCodeBo createCode(String uid,Integer appid,String userip){
if(StringUtils.isBlank(uid) || null == appid){
return null;
}
SsoCodeBo codeBo = new SsoCodeBo();
codeBo.setUid(uid);
codeBo.setAppid(appid);
//创建code授权码
String id = UUIDGenerator.getInstance().generate() + "-" + SsoCodeBo.SSO_CODE_NAMESPACE;
codeBo.setId(id);
//设置开始时间
long creatTime = System.currentTimeMillis();
codeBo.setCreatetime(new Date(creatTime));
codeBo.setIp(userip);
//设置结束时间
long expiresTime = creatTime + (TimeUnit.MINUTES.toSeconds(ssocode_expiration)*1000);
codeBo.setExpires(new Date(expiresTime));
ssoCodeCache.putCode(codeBo.getId(),codeBo);
return codeBo;
}
/**
* 校验单点登录授权码(缓存提取)
* @param codeid 授权码id
* @param appid 应用id
* @param userip 用户ip(暂不启用)
* @return
*/
public Result<SsoCodeBo> valiCode(@NotNull String codeid,@NotNull Integer appid,@NotNull String userip){
if(StringUtils.isBlank(codeid) || null == appid){
return Result.create(CommonError.illegal_args);
}
SsoCodeBo codeBo = ssoCodeCache.getCode(codeid);
if (null == codeBo){
return Result.create(IdmError.auth_ssocode_null);
}
if (!appid.equals(codeBo.getAppid())){
return Result.create(IdmError.auth_appcode_mismatching);
}
//时间对比,校验是否过期
Date date = codeBo.getExpires();
long currentTime = System.currentTimeMillis();
long value = date.getTime() - currentTime;
if(value < 0){
//临时授权码超时失效
return Result.create(IdmError.auth_ssocode_outtime);
}
//TODO ip校验.目前先不做一致性校验
//验证成功,清理缓存中的数据
ssoCodeCache.resetCode(codeid);
return Result.create(codeBo);
}
}java
package com.commnetsoft.idm.util.sso;
import com.commnetsoft.core.cache.CacheDuration;
import com.commnetsoft.idm.model.sso.TicketPo;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
/**
* 功能描述: Ticket缓存工具类
* @Param:
* @Return:
* @Author: yzg
* @Date: 2020/11/12 9:37
*/
@Service
public class TicketCacheUtil {
@Cacheable(value = "ticket",key = "#key",unless = "#result == null")
@CacheDuration(var="#{idmConstantConfig.MILLIS_PER_MINUTE() * idmConstantConfig.ticketDeadtime}")
public TicketPo getTicket( String key){
return null;
}
@CachePut(value = "ticket",key = "#key")
@CacheDuration(var="#{idmConstantConfig.MILLIS_PER_MINUTE() * idmConstantConfig.ticketDeadtime}")
public TicketPo putTicket(String key, TicketPo ticket){
return ticket;
}
/**
* 销毁缓存
*/
@CacheEvict(value = "ticket",key = "#key")
public void destroyTicket(String key){
//ignore
}
}java
package com.commnetsoft.idm.util.sso;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.core.cache.CacheDuration;
import com.commnetsoft.idm.model.sso.TokenPo;
import com.commnetsoft.idm.service.sso.TokenService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
/**
* Token缓存工具类
* @ClassName TokenCache
* @Author wyj
* @Date 2020/9/2 0002 17:01
* @Version 1.0
*/
@Service
@RefreshScope
public class TokenCache {
private static final Logger logger = LoggerFactory.getLogger(TokenCache.class);
@Value("#{idmConstantConfig.token_expiration}")
private Integer token_expiration;
@Autowired
private TokenService tokenService;
@CachePut(value = "token",key = "#tokenId")
@CacheDuration(var="#{idmConstantConfig.MILLIS_PER_MINUTE() * idmConstantConfig.token_expiration}")
public TokenPo putToken(String tokenId, TokenPo tokenPo){
return tokenPo;
}
@Cacheable(value = "token",key = "#tokenId")
@CacheDuration(var="#{idmConstantConfig.MILLIS_PER_MINUTE() * idmConstantConfig.token_expiration}")
public TokenPo getToken(String tokenId){
if (StringUtils.isBlank(tokenId)){
return null;
}
TokenPo tokenPo = tokenService.queryByKey(tokenId);
return tokenPo;
}
@CacheEvict(value = "token",key = "#tokenId")
public void deleteToken(String tokenId){
}
}java
package com.commnetsoft.idm.util;
import com.commnetsoft.commons.utils.EncryptUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.security.Key;
/**
* idm内部使用AES解密方法,使用加解密私钥为commnetsoft.*_zxg%#@
*/
@Service
public class IdmAESUtil extends EncryptUtils {
public final static Logger logger = LoggerFactory.getLogger(IdmAESUtil.class);
public static String private_key = "commnetsoft.*_zxg%#@";
/**
* 使用秘钥加密
* @param str
* @param key
* @return
*/
public String encoder(String str, String key) {
String result = "";
try{
Key key_ = AES.getSunKey(key);
result = AES.encrypt(key_, str);
} catch (Exception e) {
logger.error("AES加密失败", e);
}
return result;
}
/**
* 使用默认秘钥加密
* @param str
* @return
*/
public String encoder(String str) {
return encoder(str, private_key);
}
/**
* 按照key解密
* @param str
* @param key
* @return
*/
public String decoder(String str, String key) {
String result = "";
try{
Key key_ = AES.getSunKey(key);
result = AES.decrypt(key_, str);
} catch (Exception e) {
logger.error("AES解密失败,密文:{}", str, e);
}
return result;
}
/**
* 按照默认key解密
* @param str
* @return
*/
public String decoder(String str) {
return decoder(str,private_key);
}
}java
package com.commnetsoft.idm.util;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.idm.constants.IdmConstant;
import com.commnetsoft.idm.model.sso.AppSsoCfgPo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import javax.validation.constraints.NotNull;
import java.util.Calendar;
/**
* 单点登录配置
* @ClassName IdmAppSsoUtil
* @Author wyj
* @Date 2020/8/25 0025 10:06
* @Version 1.0
*/
@Service
@RefreshScope
public class IdmAppSsoUtil {
public final static Logger logger = LoggerFactory.getLogger(IdmAppSsoUtil.class);
@Value("#{idmConstantConfig.sso_imgcode_enable}")
private Integer sso_imgcode_enable;
/**
* 判断用户当前登录是否满足资源登录要求等级
* @param app 应用实体类
* @param userlevel 账户等级
* @return
*/
public boolean isAllowLevel(AppSsoCfgPo app, Integer userlevel){
if(sso_imgcode_enable.equals(IdmConstant.TRUE)){
return userlevel >= app.getAllowlevel();
}
return true;
}
/**
* 用户是否被授权登录接入资源
* @param app 应用实体类
* @param accountid 账户id
* @return 1.有权限接入:true,2.无权限看:false
*/
public static boolean accessAllowed(AppSsoCfgPo app, String accountid){
if(null == app || StringUtils.isBlank(accountid)){
return false;
}
if(isPublic(app)){
return true;
}
boolean result = false;
//TODO 非公开应用中是否绑定相关账户允许其访问
return result;
}
/**
* 判断是否公开访问该应用
* @param app 应用实体类
* @return 1.公开:true 2.非公开:false
*/
public static boolean isPublic(@NotNull AppSsoCfgPo app){
if(IdmConstant.TRUE.equals(app.getIspublic())){
return true;
}else{
return false;
}
}
/**
* 是否启用自定义授权
* @return
*/
public static boolean isAppNeedAuth(AppSsoCfgPo appSsoCfg){
if(null==appSsoCfg){
return false;
}
if(IdmConstant.TRUE.equals(appSsoCfg.getIsneedauth())){
return true;
}
return false;
}
/**
* 是否为允许访问的时间段,允许为true
* @param param AppSsoCfgPo应用实体类
* @return
*/
public static boolean isAllowTime(AppSsoCfgPo param){
if(param == null|| param.getAllowtype() == null){
return false;
}
if(param.getAllowtype().equals(AppSsoCfgPo.ALLOW_TYPE_OPEN)){
return true;
}
if( param.getAllowstarttime() == null || param.getAllowendtime() == null){
return false;
}
String allowstarttime = param.getAllowstarttime();
String allowendTime = param.getAllowendtime();
try {
String[] str1 = allowstarttime.split(":");
String[] str2 = allowendTime.split(":");
int startime = Integer.parseInt(str1[0])*60+Integer.parseInt(str1[01]);
int endtime = Integer.parseInt(str2[0])*60+Integer.parseInt(str2[01]);
Calendar now = Calendar.getInstance();
int nowtime = now.get(Calendar.HOUR_OF_DAY)*60+now.get(Calendar.MINUTE);
if((endtime-startime) <= 0){
endtime += 24*60;
}
if(((nowtime-startime)>=0)&&((endtime-nowtime)>=0)){
return true;
}else {
return false;
}
} catch (Exception e) {
logger.error("判断是否为可访问时间段失败",e);
return false;
}
}
}java
package com.commnetsoft.idm.util;
import com.commnetsoft.commons.utils.StringUtils;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
/**
* 主要用于提交参数
* @ClassName IdmParameterUtil
* @Author wyj
* @Date 2020/9/8 0008 18:16
* @Version 1.0
*/
public class IdmParameterUtil {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(IdmParameterUtil.class.getName());
public IdmParameterUtil() {
}
/**
* 解析参数为map。 如a=1&b=2 则返回 a:1 b:2的map
* @param extargs a=1&b=2
* @return
*/
public static Map<String, String> parseArgs(String extargs){
Map<String, String> result = new HashMap<String, String>();
if(StringUtils.isBlank(extargs)|| extargs.indexOf('=')<0){
return result;
}
String[] args = extargs.split("&");
int i = 0;
for (String temp : args) {
i = temp.indexOf('=');
if(i>0){
result.put(temp.substring(0, i), temp.substring(i+1));
}
}
return result;
}
}java
package com.commnetsoft.idm.util;
import com.commnetsoft.commons.IErrorCode;
import com.commnetsoft.commons.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogUtils {
private static final Logger logger = LoggerFactory.getLogger(LogUtils.class);
/**
* 组装参数名和参数值,键值对匹配
* @param argsName
* @param argsValue
* @return
*/
public static String getLogMsg(String[] argsName,String[] argsValue){
if(argsName.length == 0||argsValue.length == 0){
return null;
}
if(argsName.length != argsValue.length){
return null;
}
StringBuffer record = new StringBuffer();
for (int i = 0; i < argsName.length; i++) {
record.append(argsName[i]).append("=").append(argsValue[i]);
if (i < (argsName.length - 1)) {
record.append(",");
}
}
return record.toString();
}
/**
* debug级别日志记录,接口启动时候开启debug记录
* @param methodName
* @param record
*/
public static void recordDebugLog(String methodName,String record){
if(logger.isDebugEnabled()){
if(StringUtils.isBlank(record)){
logger.debug("提示信息:【"+methodName+"】接入开始。"+"参数未设置或参数名与参数值不匹配");
}else{
logger.debug("提示信息:【"+methodName+"】接入开始。参数:"+record);
}
}
}
/**
* warn级别日志记录
* @param errorCode
* @param methodName
* @param record
*/
public static void recordWarnLog(IErrorCode errorCode,String methodName,String record){
if(StringUtils.isBlank(record)){
logger.warn(methodName+"。错误信息:"+errorCode+"。参数未设置或参数名与参数值不匹配");
}else{
logger.warn(methodName+"。错误信息:"+errorCode+"("+errorCode.getDesc()+")。参数:"+record);
}
}
/**
* error级别日志记录
* @param errorCode
* @param methodName
* @param record
*/
public static void recordErrorLog(IErrorCode errorCode, String methodName, String record){
if(StringUtils.isBlank(record)){
logger.error(methodName+"。错误信息:"+errorCode+"。参数未设置或参数名与参数值不匹配");
}else{
logger.error(methodName+"。错误信息:"+errorCode+"("+errorCode.getDesc()+")。参数:"+record);
}
}
}java
package com.commnetsoft.idm.util;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.security.KeyPair;
@Service
public class RSACacheUtil {
private static final Logger logger = LoggerFactory.getLogger(RSACacheUtil.class);
public transient final static String NAMESPACE_PRIVATE_KEY = "privatekey";
public transient final static String NAMESPACE_PUBLIC_KEY = "publickey";
/**
* 初始化rsa公钥
* @param keyPair
* @param namespace
* @return
*/
@CachePut(value = "rsa",key = "#namespace")
public String initPublicKey (KeyPair keyPair, String namespace){
byte[] b = keyPair.getPublic().getEncoded();
return Base64.encodeBase64String(b);
}
/**
* 初始化rsa私钥
* @param keyPair
* @param namespace
* @return
*/
@CachePut(value = "rsa",key = "#namespace")
public String initPrivateKey (KeyPair keyPair,String namespace){
byte[] b = keyPair.getPrivate().getEncoded();
return Base64.encodeBase64String(b);
}
/**
* 获取RSA 公私钥(只从缓存中取)
* @return
*/
@Cacheable(value = "rsa",key = "#namespace")
public String getRsakeyBase64(String namespace){
return null;
}
}java
package com.commnetsoft.idm.util;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.core.cache.CacheLock;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
@Service
public class RSAUtil {
private static final Logger logger = LoggerFactory.getLogger(RSAUtil.class);
@Autowired
private RSACacheUtil rsaCacheUtil;
public String initRSAKey(String namespace){
//开启CacheLock分布式锁
try (CacheLock c = CacheLock.of("RSALock")){
c.lock();
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA", "BC");
keyPairGen.initialize(1024);
KeyPair keyPair = keyPairGen.generateKeyPair();
//初始化缓存RSA公钥私钥
String publicKey = rsaCacheUtil.initPublicKey(keyPair,RSACacheUtil.NAMESPACE_PUBLIC_KEY);
String privateKey = rsaCacheUtil.initPrivateKey(keyPair,RSACacheUtil.NAMESPACE_PRIVATE_KEY);
if (StringUtils.equals(namespace,RSACacheUtil.NAMESPACE_PRIVATE_KEY)){
return privateKey;
} else if (StringUtils.equals(namespace,RSACacheUtil.NAMESPACE_PUBLIC_KEY)){
return publicKey;
}
} catch (Exception e) {
logger.error("RSA 算法生成公私钥对异常",e);
}
return null;
}
/**
* 获取公钥
* @return
*/
public String getPublickey(){
String publicKey = rsaCacheUtil.getRsakeyBase64(RSACacheUtil.NAMESPACE_PUBLIC_KEY);
if (StringUtils.isBlank(publicKey)){
publicKey = initRSAKey(RSACacheUtil.NAMESPACE_PUBLIC_KEY);
}
return publicKey;
}
/**
* 获取私钥
* @return
*/
public String getPrivateKey(){
String privateKey = rsaCacheUtil.getRsakeyBase64(RSACacheUtil.NAMESPACE_PRIVATE_KEY);
if (StringUtils.isBlank(privateKey)){
privateKey = initRSAKey(RSACacheUtil.NAMESPACE_PRIVATE_KEY);
}
return privateKey;
}
/**
* 使用默认的公钥,进行RAS明文加密。 返回结果为BASE64编码的字符串
* @param data
* @return
* @throws Exception
*/
public String encryptBase64(String data) throws Exception{
// 格式化公钥
byte[] publicKeyb = Base64.decodeBase64(getPublickey());
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKeyb);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
byte[] b = encrypt(data, publicKey);
return Base64.encodeBase64String(b);
}
/**
* 使用默认的私钥,进行RAS密文解密。 密文是base64编码的字符串
* @param data base64编码的字符串
* @return
* @throws Exception
*/
public String decryptBase64(String data) throws Exception{
// 格式化私钥
byte[] privateKeyb = Base64.decodeBase64(getPrivateKey());
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKeyb);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
byte[] b = Base64.decodeBase64(data);
return decrypt(b, privateKey);
}
/**
* RSA 加密
* @param data 明文
* @param publicKey 公钥
* @return
* @throws Exception
* @throws NoSuchPaddingException
* @throws NoSuchProviderException
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @throws UnsupportedEncodingException
* @throws BadPaddingException
* @throws IllegalBlockSizeException
*/
public static byte[] encrypt(String data, PublicKey publicKey) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException{
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] b =cipher.doFinal(data.getBytes("UTF-8"));
return b;
}
/**
* RSA 解密
* @param data 密文
* @param privateKey 私钥
* @return
* @throws Exception
* @throws NoSuchPaddingException
* @throws NoSuchProviderException
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @throws BadPaddingException
* @throws IllegalBlockSizeException
* @throws UnsupportedEncodingException
*/
public static String decrypt(byte[] data, PrivateKey privateKey) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException{
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding", "BC");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
//模长
return new String(cipher.doFinal(data),"UTF-8");
}
}java
package com.commnetsoft.idm.util;
import java.net.InetAddress;
/**
* UUID生成器
*/
public class UUIDGenerator {
private static final int IP;
private static short counter = (short) 0;
private static final int JVM = (int) (System.currentTimeMillis() >>> 8);
private static UUIDGenerator uuidgen = new UUIDGenerator();
/**
* 单利模式
*/
private UUIDGenerator() {
}
static {
int ipadd;
try {
ipadd = toInt(InetAddress.getLocalHost().getAddress());
}
catch (Exception e) {
ipadd = 0;
}
IP = ipadd;
}
/**
* 获取对象实例
* @param @return
* @return UUIDGenerator
*/
public static UUIDGenerator getInstance() {
return uuidgen;
}
public static int toInt(byte[] bytes) {
int result = 0;
for (int i = 0; i < 4; i++) {
result = (result << 8) - Byte.MIN_VALUE + (int) bytes[i];
}
return result;
}
protected String format(int intval) {
String formatted = Integer.toHexString(intval);
StringBuffer buf = new StringBuffer("00000000");
buf.replace(8 - formatted.length(), 8, formatted);
return buf.toString();
}
protected String format(short shortval) {
String formatted = Integer.toHexString(shortval);
StringBuffer buf = new StringBuffer("0000");
buf.replace(4 - formatted.length(), 4, formatted);
return buf.toString();
}
protected int getJVM() {
return JVM;
}
protected synchronized short getCount() {
if (counter < 0) {
counter = 0;
}
return counter++;
}
protected int getIP() {
return IP;
}
protected short getHiTime() {
return (short) (System.currentTimeMillis() >>> 32);
}
protected int getLoTime() {
return (int) System.currentTimeMillis();
}
/**
* 生成UUID方法
* @param @return
* @return String
* @author chenli
*/
public synchronized String generate() {
return new StringBuffer(32).append(format(getIP())).append(format(getJVM()))
.append(format(getHiTime())).append(format(getLoTime()))
.append(format(getCount())).toString();
}
}java
package com.commnetsoft.idm.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
public class ZIPUtils {
/**
* 使用gzip进行压缩
*/
public static String compress(String primStr) {
if (primStr == null || primStr.length() == 0) {
return primStr;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPOutputStream gzip = null;
try {
gzip = new GZIPOutputStream(out);
gzip.write(primStr.getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (gzip != null) {
try {
gzip.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return Base64.getEncoder().encodeToString(out.toByteArray());
}
/**
* 使用gzip进行解压缩
*/
public static String uncompress(String compressedStr) {
if (compressedStr == null) {
return null;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = null;
GZIPInputStream ginzip = null;
byte[] compressed = null;
String decompressed = null;
try {
compressed = Base64.getDecoder().decode(compressedStr);
in = new ByteArrayInputStream(compressed);
ginzip = new GZIPInputStream(in);
byte[] buffer = new byte[1024];
int offset = -1;
while ((offset = ginzip.read(buffer)) != -1) {
out.write(buffer, 0, offset);
}
decompressed = out.toString();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ginzip != null) {
try {
ginzip.close();
} catch (IOException e) {
}
}
if (in != null) {
try {
in.close();
} catch (IOException e) {
}
}
try {
out.close();
} catch (IOException e) {
}
}
return decompressed;
}
/**
* 使用zip进行压缩
* @param str 压缩前的文本
* @return 返回压缩后的文本
*/
public static final String zip(String str) {
if (str == null)
return null;
byte[] compressed;
ByteArrayOutputStream out = null;
ZipOutputStream zout = null;
String compressedStr = null;
try {
out = new ByteArrayOutputStream();
zout = new ZipOutputStream(out);
zout.putNextEntry(new ZipEntry("0"));
zout.write(str.getBytes());
zout.closeEntry();
compressed = out.toByteArray();
compressedStr = Base64.getEncoder().encodeToString(compressed);
} catch (IOException e) {
compressed = null;
} finally {
if (zout != null) {
try {
zout.close();
} catch (IOException e) {
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
}
}
}
return compressedStr;
}
/**
* 使用zip进行解压缩
* @param compressedStr 压缩后的文本
* @return 解压后的字符串
*/
public static final String unzip(String compressedStr) {
if (compressedStr == null) {
return null;
}
ByteArrayOutputStream out = null;
ByteArrayInputStream in = null;
ZipInputStream zin = null;
String decompressed = null;
try {
byte[] compressed = Base64.getDecoder().decode(compressedStr);
out = new ByteArrayOutputStream();
in = new ByteArrayInputStream(compressed);
zin = new ZipInputStream(in);
zin.getNextEntry();
byte[] buffer = new byte[1024];
int offset = -1;
while ((offset = zin.read(buffer)) != -1) {
out.write(buffer, 0, offset);
}
decompressed = out.toString();
} catch (IOException e) {
decompressed = null;
} finally {
if (zin != null) {
try {
zin.close();
} catch (IOException e) {
}
}
if (in != null) {
try {
in.close();
} catch (IOException e) {
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
}
}
}
return decompressed;
}
public static void main(String[] args) {
String str = "eyJraWQiOm51bGwsImFsZyI6IlJTMjU2In0.eyJpc3MiOiJjb21tbmV0c29mdEpXVCIsInN1YiI6IntcImlwXCI6XCIxOTIuMTY4LjEuMTc5XCIsXCJfaWRcIjpcIjQwMjg4MTMzNzM1NWU3ODAwMTczNTYyZGM0MDcwMDAwXCJ9IiwiYXVkIjoiY29tbW5ldHNvZnRXZWIiLCJleHAiOjE1OTUzMDcyMzMsIm5iZiI6MTU5NTMwMDAzMywiaWF0IjoxNTk1MzAwMDMzLCJqdGkiOiJ6Zm1KSnhSMnpMdjVJclZXT3dpNDBBIn0.hvQgy0vfcA7RSY2_3W-PYye5bT30E0cRl-6_bTw4u4fQG_Or3k_z8jr588yS38JDrGtSyAw6KqwtvUaXv8Zdihy6QwswEE9-VLmLId6g82n9aDXZFaUSJV90PDK8-KfmHZO2s6jXg2lYSwgmvOEvtwG3JqV4ApqmqgyvvdCZLO7MtdkahgkiOCTxef9XUq_3Gd7ebsWtIFgrjuCL-Dw8EXkt2CxRtOA8V8e0BfX7u4sMjCiRQ-HPdayg4Ghz8irttFJwvTcfejZ9vAZOblRHj3yzLj3vjL_lkuScKTa0Ef7iT3xTR42BBMvo0_ojKt01i18AbVQeVkv-mzmyBpQygw";
System.out.println("原字符串:" + str);
System.out.println("原长度:" + str.length());
String gcompress = ZIPUtils.compress(str);
System.out.println("GZip压缩后字符串:" + gcompress);
System.out.println("GZip压缩后字符串长度:" + gcompress.trim().length());
String compress = ZIPUtils.zip(str);
System.out.println("Zip压缩后字符串:" + compress);
System.out.println("Zip压缩后字符串长度:" + compress.trim().length());
String gstring = ZIPUtils.uncompress(gcompress);
System.out.println("GZip解压缩后字符串:" + gstring);
String string = ZIPUtils.unzip(compress);
System.out.println("GZip解压缩后字符串:" + string);
System.out.println("解压缩前字符串:" + str);
}
}