JWT生成token登录退出学习使用

登录接口

@AuditLogEnregistor(module=AuditLogEvent.MODULE_LOGON,action="登录")
    @ApiOperation(value="用户登录", httpMethod="POST")
    @RequestMapping(value="/login", method=RequestMethod.POST)
    public ApiResultDTO<AccessTokenUser> login(@RequestBody LoginVO vo, HttpServletRequest hreq,
            HttpServletResponse hrep){
        return RestAPITemplate.restapi(() -> {
            String userIP = hreq.getRemoteAddr();
            //账号密码解密
            LoginVO newvo;
            if(StringUtils.isNotBlank(vo.getSm4Json())) {
                newvo = JsonConverter.jsonStrToObject(SM4Utils.decryptBase64ToStr(vo.getSm4Json(), null), LoginVO.class);
            }else {
                newvo = vo;
            }
            PrivilegesUser loginUser = null;
            try {
                 loginUser = ThreadLocalCache.fetchAPIData(null,
                     () -> {
                         return sysGatewayService.getPrivilegesUserByAccount_dontCache(newvo.getAccount(), true);
                     }
                 );
            } catch (Exception e) {
                logger.error("获取用户服务出错", e);
                throw new RuntimeException("获取用户服务出错");
            }
            if(loginUser==null){
                throw new RuntimeException("无效的用户名");
            }
            validUserPassword(loginUser, newvo.getPassword(), userIP);
            AccessTokenUser tokenUser=loginUser.buildAccessTokenUser(null);
            int maxInactiveInterval;
            boolean forced = false;
            if (XysdConstants.TOKEN_FORMTYPE_APP.equals(vo.getFromType())) {
                forced = true;
                maxInactiveInterval = Integer.parseInt(BaseConstants.getProperty("maxInactiveIntervalWithApp",(60*24)+""));
            } else {
                maxInactiveInterval = Integer.parseInt(BaseConstants.getProperty("maxInactiveInterval",(60*24)+""));
            }
            AccessTokenGenerateVO tokenGenerateVO = new AccessTokenGenerateVO(tokenUser, XysdConstants.TOKEN_FORMTYPE_PC,
                    null, forced, maxInactiveInterval);
            String token=ThreadLocalCache.fetchAPIData(null, ()->{
                return tokenService.generateAccessToken(tokenGenerateVO);
            }, "生成访问令牌出错,");
            tokenUser.setAccessToken(token);
            tokenUser.addAttr(AccessTokenUser.ATTR_FROM_TYPE,(forced?XysdConstants.TOKEN_FORMTYPE_APP:XysdConstants.TOKEN_FORMTYPE_PC));
            //返回密码超期提醒
            if (ValueObjectEntity.SYSCODE_HDXF.equals(BaseConstants.getProperty("syscode",null))){
                Long begin = loginUser.getPwdLastUpdateTime();
                long end = new Date().getTime();
                if(begin!=null&&end>=begin) {
                    //转换为天
                    long days = (end - begin )/1000/60/60/24;
                    if(days>90) {
                        OtherData userInfo = tokenUser.getUserInfo();
                        if(userInfo==null) tokenUser.setUserInfo(new OtherData());
                        userInfo.put("pwdRemind", "您已连续使用该口令"+days+"天,请尽快更换口令。");
                    }
                }
            }
            //添加该用户其他机构的用户信息
            this.addOtherOrgUsers(tokenUser,token);

            LoginInitUtils.afterLogin(true, hreq, hrep, tokenUser);
            return tokenUser;
        });
    }

   private void validUserPassword(PrivilegesUser privilegesUser, String password, String userIP) {
        String errorMsg=null;
        AuditLogEvent auditLogEvent = null;
        if (privilegesUser.isLocked()){
            if (privilegesUser.hasResource(AuditLogEvent.CATEGORY_SAFETYMANAGER)){
                //更新时间
                long begin = privilegesUser.getLockedTime();
                long end = new Date().getTime();
                //转换为分
                long minute = (end - begin )/1000/60;
                if (minute<30){
                    //未超过30分钟
                    long timeMin=30-minute;
                    auditLogEvent=new AuditLogEvent(Utils.getUUID(""),new Date(),AuditLogEvent.MODULE_LOGON
                            ,"登录失败:用户:"+privilegesUser.getUser().getName()+"已被锁定,"+timeMin+"分后解锁。"
                            ,"/login",userIP,privilegesUser.buildAccessTokenUser(null),null,"失败",null,null);
                    errorMsg="登录失败:该用户已被锁定,"+timeMin+"分后解锁。";
                }else {
                    sysGatewayService.unLockedUser(privilegesUser.getUser().getId());
                }
            }else{
                auditLogEvent=new AuditLogEvent(Utils.getUUID(""),new Date(),AuditLogEvent.MODULE_LOGON
                        ,"登录失败:用户:"+privilegesUser.getUser().getName()+"已被锁定","/login"
                        ,userIP,privilegesUser.buildAccessTokenUser(null),null,"失败",null,null);
                errorMsg="登录失败次数过多,已被锁定,请联系管理员解锁";
            }
        }else if(privilegesUser.getPassword()!=null){
            logger.info("=====开始加密======:["+password+"]");
            if(!privilegesUser.getPassword().equals(SM3Utils.sm3(password))) {
                //如果用户密码错误
                sysGatewayService.addUserPwdWrongCountById_dontCache(privilegesUser.getUser().getId());
                int nowWrongPwdCount=privilegesUser.getWrongPwdCount()+1;
                if (nowWrongPwdCount>=5){
                    auditLogEvent=new AuditLogEvent(Utils.getUUID(""),new Date(),AuditLogEvent.MODULE_LOGON
                            ,"登录失败:用户:"+privilegesUser.getUser().getName()+"密码连续输入错误次数超过"
                                    +5+"次,该用户已被锁定。"
                                    ,"/login",userIP,privilegesUser.buildAccessTokenUser(null),null,"失败",null,null);
                    sysGatewayService.lockedUser(privilegesUser.getUser().getId());
                    errorMsg="登录失败次数过多,已被锁定,请联系管理员解锁";
                }else {
                    auditLogEvent=new AuditLogEvent(Utils.getUUID(""),new Date(),AuditLogEvent.MODULE_LOGON
                            ,"登录失败用户:"+privilegesUser.getUser().getName()+"密码错误 。","/login"
                            ,userIP,privilegesUser.buildAccessTokenUser(null),null,"失败",null,null);
                    errorMsg="密码连续输入错误"+nowWrongPwdCount+"次,超过"+5+"次后用户锁定。";
                }
            }else {
                sysGatewayService.initUserPwdWrongCount(privilegesUser.getUser().getId());
            }
        }else if(privilegesUser.getPassword()==null && null==password){
            sysGatewayService.initUserPwdWrongCount(privilegesUser.getUser().getId());
        }else {
            errorMsg = "无效的用户";
        }
        if (auditLogEvent!=null){
            DomainEventPublisherFactory.getRegisteredPublisher().publishEvent(auditLogEvent);
        }
        if(StringUtils.isNotBlank(errorMsg)){
            throw new RuntimeException(errorMsg);
        }
    }

private void addOtherOrgUsers(AccessTokenUser tokenUser,String token){
        if(!ValueObjectEntity.SYSCODE_GJJTXF.equals(BaseConstants.getProperty("syscode",null))){
            return;
        }
        if(StringUtils.isBlank(tokenUser.obtainSyncUserRealId())){
            return;
        }
        List<PrivilegesUser> loginUsers;//该用户 在所有机构下的用户信息
        try {
            loginUsers = ThreadLocalCache.fetchAPIData(null, new IDataFetch<ApiResultDTO<List<PrivilegesUser>>>() {
                @Override
                public ApiResultDTO<List<PrivilegesUser>> fetchData() {
                    return sysGatewayService.getAllPrivilegesUserBySyncPartyIdAndDataFrom(tokenUser.obtainSyncUserRealId(), null);
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("获取用户服务出错");
        }
        if (CollectionUtils.isEmpty(loginUsers)) {
            return;
        }

        //该用户在 其他机构下的用户信息
        List<PrivilegesUser> otherLoginUsers = loginUsers.stream()
                .filter(l->!l.getUser().getId().equals(tokenUser.getUserId()))
                .collect(Collectors.toList());

        if(CollectionUtils.isNotEmpty(otherLoginUsers)){
            if(tokenUser.getUserInfo()==null){
                tokenUser.setUserInfo(new OtherData());
            }
            List<SysDataSimpleValObj> otherOrgUsers = otherLoginUsers.stream()
                    .map(o->{
                        o.getUser().putOtherData("ticket",token);
                        o.getUser().putOtherData("syncPartyId", tokenUser.obtainSyncUserRealId());
                        o.getUser().putOtherData("org",o.getOrg());
                        o.getUser().putOtherData("dept",o.getDept());
                        return o.getUser();
                    })
                    .collect(Collectors.toList());
            tokenUser.getUserInfo().put(AccessTokenUser.ATTR_OTHER_ORG_USER,otherOrgUsers);
        }
    }

根据用户信息生成token

@ApiOperation(value="根据信息生成访问令牌", httpMethod="POST")
    @RequestMapping(value="/generate", method=RequestMethod.POST)
    public ApiResultDTO<String> generateAccessToken(@RequestBody AccessTokenGenerateVO vo){
        return RestAPITemplate.restapi(()->{
            return tokenService.createAccessToken(vo.getUser(), vo.getUser().getAccount(),
                    vo.getFromType(), vo.getDeviceId(), vo.isForced(), vo.getExpireMinutes());
        });
    }

public String createAccessToken(AccessTokenUser user, String account, String fromType,
            String deviceId, boolean forced, int expireMinutes
            ) {
        //判断是否 踢出用户
        if("before".equals(BaseConstants.getProperty("kickOutUserType",""))){
            //删除历史令牌
            tokenRepository.deleteTokenBy(user.getUser().getId(),fromType);
        }else if("after".equals(BaseConstants.getProperty("kickOutUserType",""))){
            List<Token> exists=tokenRepository.findValidTokensByUserId(user.getUser().getId(), fromType);
            if(exists!=null&&exists.size()>0){
                throw new RuntimeException("您已在其他地址登录!");
            }
        }
        if(!XysdConstants.TOKEN_FORMTYPE_PC.equals(fromType)&&forced){//移动端只允许一个登陆  当用户需要强制登录时  清除另一台设备的令牌
            List<String> exists=tokenRepository.findDeviceIdsBy(user.getUser().getId(), fromType);
            if(exists!=null&&exists.size()>0){
                StringBuffer deviceIds=new StringBuffer();
                for (String t : exists) {
                    if(StringUtils.isNotBlank(t)
                            &&!t.equals(deviceId)){
                        if(deviceIds.length()>0){
                            deviceIds.append(",");
                        }
                        deviceIds.append(t);
                    }
                }
                tokenRepository.deleteTokenBy(user.getUser().getId(), fromType);
                if(XysdConstants.TOKEN_FORMTYPE_APP.equals(fromType)&&deviceIds.length()>0){
                    
                }
            }
        }
        try {
            if(!XysdConstants.TOKEN_FORMTYPE_PC.equals(fromType)&&!forced){
                tokenRepository.deleteTokenBy(user.getUser().getId(),fromType);
            }
            Token token=Token.buildToken(fromType, expireMinutes, user, account, deviceId);
            tokenRepository.create(token);
            
            AccessTokenUserCache ac=new AccessTokenUserCache(token);
            getCache().put(ac.getAccess_token(), ac);
            
            return token.getId();
        } catch (Exception e) {
            if(e.getCause() instanceof ConstraintViolationException){
                throw new RuntimeException("令牌已存在");
            }
            e.printStackTrace();
            throw e;
        }
    }

构建token

public static Token buildToken(String fromType, int expireMinutes, AccessTokenUser user,
            String account, String deviceId) {
        String tokenId = Utils.getUUID("");
        if(StringUtils.isNotBlank(user.getAccessToken())) {//创建指定令牌
            tokenId=user.getAccessToken();
        }
        String tokenSgin = user.getUser().getId()+"_"+fromType;
        //app/小程序 登录时同一个用户只允许登录一个设备 所以从数据库令牌唯一性做处理
        if(!XysdConstants.TOKEN_FORMTYPE_APP.equals(fromType)
                ||!XysdConstants.TOKEN_FORMTYPE_WECHATAPPLET.equals(fromType)
                ){
            tokenSgin+="_"+Utils.getUUID("");
        }
        Token token = new Token(tokenId, expireMinutes, user.getUser(), account,
                fromType, deviceId, tokenSgin, JsonConverter.toJsonStr(user.getUserInfo())
                );
        
        return token;
    }

生成内部token

@ApiOperation(value="验证令牌并获取用户", httpMethod="POST")
    @RequestMapping(value="/generate", method=RequestMethod.POST)
    public ApiResultDTO<String> generateAccessToken(@RequestBody AccessTokenUser user){
    	return RestAPITemplate.restapi(()->{
    		return tokenService.generateInnerToken(user);
    	});
    }
public String generateInnerToken(AccessTokenUser user) {
        Map<String,String> u=new HashMap<String,String>();
        if(user!=null) {
            u.put("user", JsonConverter.toJsonStr(user));
        }
        long expireTime = 3*60*1000;
        user.setExpireTime(System.currentTimeMillis()+expireTime);
        return JWTUtils.createToken(u, expireTime);
    }

JWTUtils工具类

private static final byte[] SECRET = "XX#$%()(#*!()!KL<>?N<:{LWPW".getBytes();

	static {
		Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
	}

	/**
	 * 生成jwtToken
	 * @param claims
	 * @param maxLife
	 * @return
	 */
	public static String createToken(Map<String,String> claims, long maxLife) {
	    try {
	        JWT jwt=JWT.create();
	        //jwt.setHeader(JWTHeader.TYPE, "JWT");//默认值
	        jwt.setHeader(JWTHeader.ALGORITHM, "HS256");
	        jwt.setSigner(JWTSignerUtil.createSigner("HS256", SECRET));
	        
	        if(claims!=null&&claims.size()>0){
	        	jwt.addPayloads(claims);
	        }
	        
	        jwt.setExpiresAt(new Date(System.currentTimeMillis()+maxLife));//有效时间 即改token 有效时长
	        
	        String token=jwt.sign();//加密
	        
	        return token;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("根据token获取对象失败");
        }
	}
	
	/**
	 * 获取对象
	 * @param jwt
	 * @return token
	 */
	public static Map<String,String> verifyTokenAndGetClaims(String jwt_token) {
		JWT jwt=JWT.create();
        //jwt.setHeader(JWTHeader.TYPE, "JWT");//默认值
        jwt.setHeader(JWTHeader.ALGORITHM, "HS256");
        jwt.setSigner(JWTSignerUtil.createSigner("HS256", SECRET));
	    try {
	        jwt.parse(jwt_token);
		} catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("校验jwt_token令牌失败");
        }
	    if(!jwt.verify()) {
        	throw new RuntimeException("无效的jwt_token");
        }
        
    	Map<String,String> result = new HashMap<String,String>();
    	
    	JSONObject jo=jwt.getPayloads();
    	if(jo==null||jo.size()==0) {
    		return result;
    	}
        for (Map.Entry<String,Object> d: jo.entrySet()) {
        	if(d.getValue()==null) {
        		continue;
        	}
        	result.put(d.getKey(), d.getValue().toString());
		}
        
        return result;
	}
}

退出接口

@AuditLogEnregistor(module=AuditLogEvent.MODULE_LOGON,action="退出",before=true)
    @ApiOperation(value="用户退出", httpMethod="GET")
    @RequestMapping(value="/api/logout",method=RequestMethod.GET)
    public ApiResultDTO<String> logout(HttpServletRequest hreq,HttpServletResponse hrep){
        return RestAPITemplate.restapi(new IMyLogic<String>() {
            @Override
            public String logic() {
                AccessTokenUser loginUser = new AccessTokenUserAssembler().getAccessTokenUserFromReq(hreq);
                if(loginUser!=null){
                    tokenService.removeAccessToken(loginUser.getAccessToken());
                }
                LoginInitUtils.afterLogout(true, hreq, hrep);
                return null;
            }
        });
    }
}

@ApiOperation(value="根据信息生成访问令牌", httpMethod="POST")
    @ApiImplicitParam(name="access_token",value="令牌",required=true)
    @RequestMapping(value="/remove", method=RequestMethod.POST)
    public ApiResultDTO<String> removeAccessToken(@RequestParam("access_token")String access_token){
        return RestAPITemplate.restapi(()->{
            tokenService.removeAccessToken(access_token);
            return null;
        });
    }
public void removeAccessToken(String tokenId) {
        Token token=tokenRepository.findTokenByTokenId(tokenId);
        if(token!=null) {
            tokenRepository.remove(token);
        }
        
        //获取注册中心本服务的所有实例列表
        List<ServiceInstance> serviceInstances = discoveryClient.getInstances(serviceName);
        if(CollectionUtils.isEmpty(serviceInstances)) {
            serviceInstances = discoveryClient.getInstances(StringUtils.upperCase(serviceName));
        }
        //只有本实例一个  直接清除
        if(serviceInstances.size()<=1) {
            getCache().invalidate(tokenId);
            return;
        }
        //通知所有服务清除缓存的无效令牌
        List<Future<String>> futures=new ArrayList<Future<String>>();
        ExecutorService executor=Executors.newCachedThreadPool();
        //构建http请求体
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<List<String>> req = new HttpEntity<>(Arrays.asList(tokenId), httpHeaders);
        for (ServiceInstance serviceInstance : serviceInstances) {
            futures.add(executor.submit(()->{
                try {
                    restTemplate.postForObject(serviceInstance.getUri()+"/"+serviceName+"/access_token/remove/notice",
                            req, ApiResultDTO.class);
                    return "success";
                } catch (Exception e) {
                    return "failed";
                }
            }));
        }
        executor.shutdown();
        
        //等待执行结束
        for (Future<String> future : futures) {
            try {
                future.get(5,TimeUnit.SECONDS);
            } catch (Exception e) {
            }
        }
        executor.shutdownNow();
    }

token模型

@Entity
@Table(
    name = "T_TOKEN",
    indexes = {
        @Index(name="t_token_idx1",columnList="user_id"),
        @Index(name="t_token_idx2",columnList="fromType"),
        @Index(name="t_token_idx3",columnList="expireTime"),
        @Index(name="t_token_idx4",columnList="tokenSgin",unique=true),
        @Index(name="t_token_idx5",columnList="deviceId")
    }
)
public class Token {
    
    @Id
    @Column(length=200)
    private String id;//令牌
    private int expireMinutes=-1;//令牌生成时的有效分钟数
    @Basic(optional=true)
    private Date expireTime;//令牌超时时间
    @Basic(optional=false)
    private Date lastUpdateDate;//令牌最后更新时间
    @AttributeOverrides({
        @AttributeOverride(name="id",column=@Column(name="user_id",length=100,nullable=false)),
        @AttributeOverride(name="name",column=@Column(name="user_name",length=100,nullable=false))
    })
    private SysDataSimpleValObj user;//令牌所属用户
    @Column(length=100)
    private String account;//令牌生成时所用的账号
    @Basic(optional=false)
    @Column(length=20)
    private String fromType;//令牌所属来源
    @Column(length=200)
    private String deviceId;//令牌所属设备id 移动端时 才有可能有
    @Column(length=200)
    private String tokenSgin;//令牌签名  唯一索引  用于处理同一来源只能登录一个设备
    @Lob
    @Type(type="org.hibernate.type.TextType")
    private String otherUserInfo;
   //TODO 省略get方法 
    public Token() {
        super();
        this.lastUpdateDate = new Date();
    }
    private Token(String tokenId, int expireMinutes, SysDataSimpleValObj user,
            String account, String fromType, String deviceId, String tokenSgin,
            String otherUserInfo
            ) {
        this();
        this.id = tokenId;
        this.expireMinutes = expireMinutes;
        this.expireTime = CalendarUtils.offsetMinutes(new Date(), expireMinutes);
        this.user = user;
        this.fromType = fromType;
        this.deviceId = deviceId;
        this.tokenSgin = tokenSgin;
        this.otherUserInfo = otherUserInfo;
    }
    /**
     * 构建令牌
     * @param fromType
     * @param expireTime
     * @param user
     * @param account
     * @param deviceId
     * @return
     */
    public static Token buildToken(String fromType, int expireMinutes, AccessTokenUser user,
            String account, String deviceId) {
        String tokenId = Utils.getUUID("");
        if(StringUtils.isNotBlank(user.getAccessToken())) {//创建指定令牌
            tokenId=user.getAccessToken();
        }
        String tokenSgin = user.getUser().getId()+"_"+fromType;
        //app/小程序 登录时同一个用户只允许登录一个设备 所以从数据库令牌唯一性做处理
        if(!XysdConstants.TOKEN_FORMTYPE_APP.equals(fromType)
                ||!XysdConstants.TOKEN_FORMTYPE_WECHATAPPLET.equals(fromType)
                ){
            tokenSgin+="_"+Utils.getUUID("");
        }
        Token token = new Token(tokenId, expireMinutes, user.getUser(), account,
                fromType, deviceId, tokenSgin, JsonConverter.toJsonStr(user.getUserInfo())
                );
        
        return token;
    }
    /**
     * 判断令牌是否有效
     * @return
     */
    public boolean isValidTokenNow(){
        return this.getExpireTime()==null || this.getExpireTime().getTime()>new Date().getTime();
    }
    //刷新令牌
    public void refreshExpireTime() {
        if(this.expireTime==null||this.expireMinutes<0) {
            return ;
        }
        this.expireTime = CalendarUtils.offsetMinutes(this.expireTime, this.expireMinutes);
    }
    
}

你可能感兴趣的:(学习,python,数据库)