Skip to content
标签
安全
字数
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);

    }

}