JAVA代码优化:Token验证处理

简述:

Token验证处理是指在客户端和服务端之间进行身份验证和授权的过程。在这个过程中,客户端通常会提供一个令牌(Token),用于证明其合法性和权限。服务端接收到该令牌后,需要对其进行验证,以确定该请求是否来自合法的客户端。

JWT是一种常见的Token验证处理方式。

JWT简述:

JWT(JSON Web Token)由三部分组成,它们分别是头部(Header)、载荷(Payload)和签名(Signature)。每个部分都使用Base64编码进行序列化,并使用点号(.)作为分隔符。

  1. 头部(Header):头部包含了关于JWT的元数据信息,以及指定所使用的算法的声明。常见的算法有HMAC、RSA和ECDSA等。头部通常是一个JSON对象,例如:
    {
      //"alg"表示所使用的算法(此处为HMAC SHA-256)
      "alg": "HS256",
      //"typ"表示令牌的类型(此处为JWT)
      "typ": "JWT"
    }
  2. 载荷(Payload):载荷包含了一些声明(claims),这些声明是关于实体(如用户)和其他数据的陈述。载荷可以包含预定义的声明,如"sub"(主题,表示主体的唯一标识)、"exp"(过期时间,表示令牌的有效期)、"iat"(发布时间,表示令牌的发行时间)等,也可以包含自定义的声明。载荷通常也是一个JSON对象,例如:
    {
      "sub": "1234567890",
      "name": "John Doe",
      "iat": 1516239022
    }
    
  3. 签名(Signature):签名是使用指定的算法(如HMAC、RSA等)对头部和载荷进行签名生成的一串字符串。签名用于验证令牌的完整性和真实性,以防止被篡改。签名的生成需要使用密钥(秘钥),服务端在验证令牌时也需要使用相同的密钥进行签名验证。
  4. JWT的三部分是通过点号(.)连接起来形成一个完整的令牌,例如:
    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
    .
    eyJzdWIiOiAiMTIzNDU2Nzg5MCIsIm5hbWUiOiAiSm9obiBEb2UiLCAiaWF0IjogMTUxNjIzOTAyMn0
    .
    SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
    

    token验证处理(TokenService)

  5. package com.muyuan.framework.web.service;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.TimeUnit;
    import javax.servlet.http.HttpServletRequest;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    import com.muyuan.common.constant.Constants;
    import com.muyuan.common.core.domain.model.LoginUser;
    import com.muyuan.common.core.redis.RedisCache;
    import com.muyuan.common.utils.ServletUtils;
    import com.muyuan.common.utils.StringUtils;
    import com.muyuan.common.utils.ip.AddressUtils;
    import com.muyuan.common.utils.ip.IpUtils;
    import com.muyuan.common.utils.uuid.IdUtils;
    import eu.bitwalker.useragentutils.UserAgent;
    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    
    /**
     * token验证处理
     *
     * 
     */
    @Component
    public class TokenService {
        // 令牌自定义标识
        @Value("${token.header}")
        private String header;
    
        // 令牌秘钥
        @Value("${token.secret}")
        private String secret;
    
        // 令牌有效期(默认30分钟)
        @Value("${token.expireTime}")
        private int expireTime;
    
        protected static final long MILLIS_SECOND = 1000;
    
        protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
    
        private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;
    
        @Autowired
        private RedisCache redisCache;
    
        /**
         * 获取用户身份信息
         *
         * @return 用户信息
         */
        public LoginUser getLoginUser(HttpServletRequest request) {
            // 获取请求携带的令牌
            String token = getToken(request);
            //判断一个字符串是否为非空串(详见字符串工具类)
            if (StringUtils.isNotEmpty(token)) {
                //Claims对象,它包含了Payload部分的信息,也就是我们在生成Token时添加的各种自定义属性。
                // 例如,如果我们在生成Token时添加了用户名、角色等信息,
                // 那么在解析Token时就可以通过claims.get("username")、claims.get("role")等方法来获取这些信息。
                Claims claims = parseToken(token);
                // 解析对应的权限以及用户信息
                //令牌前缀
                //public static final String LOGIN_USER_KEY = "login_user_key";
                String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
                String userKey = getTokenKey(uuid);
                //redisCache.getCacheObject获得缓存的基本对象(详见spring redis 工具类)
                LoginUser user = redisCache.getCacheObject(userKey);
                return user;
            }
            return null;
        }
    
        /**
         * 设置用户身份信息
         */
        public void setLoginUser(LoginUser loginUser)
        {
            //判断一个字符串是否为非空串(详见字符串工具类)
            if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken()))
            {
                refreshToken(loginUser);
            }
        }
    
        /**
         * 删除用户身份信息
         */
        public void delLoginUser(String token)
        {
            //判断一个字符串是否为非空串(详见字符串工具类)
            if (StringUtils.isNotEmpty(token))
            {
                String userKey = getTokenKey(token);
                //删除单个对象
                redisCache.deleteObject(userKey);
            }
        }
    
        /**
         * 创建令牌
         *
         * @param loginUser 用户信息
         * @return 令牌
         */
        public String createToken(LoginUser loginUser) {
            //IdUtils  id快速生成器(详见文章ID生成工具)
            String token = IdUtils.fastUUID();
            //登录对象类的唯一标识token
            loginUser.setToken(token);
            //设置用户代理信息
            setUserAgent(loginUser);
            //刷新令牌有效期
            refreshToken(loginUser);
            //claims是用于存放Payload部分的信息的Map对象,它包含了我们需要在Token中添加的各种自定义属性,
            // 例如用户ID、用户名、角色等
            Map claims = new HashMap<>();
            //常量令牌前缀public static final String LOGIN_USER_KEY = "login_user_key";
            claims.put(Constants.LOGIN_USER_KEY, token);
            //存放非敏感信息
            claims.put("username",loginUser.getUsername());
            claims.put("nickName",loginUser.getUser().getNickName());
            claims.put("createTime",loginUser.getUser().getCreateTime());
            return createToken(claims);
        }
    
        /**
         * 验证令牌有效期,相差不足20分钟,自动刷新缓存
         *
         * @param loginUser
         * @return 令牌
         */
        public void verifyToken(LoginUser loginUser)
        {
            //过期时间
            long expireTime = loginUser.getExpireTime();
            //当前时间
            long currentTime = System.currentTimeMillis();
            if (expireTime - currentTime <= MILLIS_MINUTE_TEN)
            {
                refreshToken(loginUser);
            }
        }
    
        /**
         * 刷新令牌有效期
         *
         * @param loginUser 登录信息
         */
        public void refreshToken(LoginUser loginUser)
        {
            //设置登录时间
            loginUser.setLoginTime(System.currentTimeMillis());
            //设置过期时间
            loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
            // 根据uuid将loginUser缓存
            String userKey = getTokenKey(loginUser.getToken());
            //储存redis详见文章(spring redis 工具类)
            redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
        }
    
        /**
         * 设置用户代理信息
         *
         * @param loginUser 登录信息
         */
        public void setUserAgent(LoginUser loginUser)
        {
            //User-Agent是HTTP协议中的一个头部信息,通常用于标识发送HTTP请求的客户端软件或代理程序的详细信息。
            // 它包含了客户端软件类型、版本号、操作系统类型、语言等信息。
            // 在Web开发中,服务器可以通过User-Agent头部信息来识别客户端的浏览器和操作系统等信息。
            UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
            //获取ip详见文章(ip获取地址类)
            String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
            //存入以下数据
            loginUser.setIpaddr(ip);
            loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
            loginUser.setBrowser(userAgent.getBrowser().getName());
            loginUser.setOs(userAgent.getOperatingSystem().getName());
        }
    
        /**
         * 从数据声明生成令牌
         *
         * @param claims 数据声明
         * @return 令牌
         */
        private String createToken(Map claims)
        {
            String token =
                    //Jwts.builder()方法创建一个JWT Builder对象,用于构建JWT Token。
                    Jwts.builder()
                    //调用setClaims(claims)方法设置JWT Token中的payload部分,即要传递的自定义信息。
                    //这里的claims参数是一个Map对象,其中包含了需要传递的键值对信息。
                    .setClaims(claims)
                    //signWith(SignatureAlgorithm.HS512, secret)方法对JWT Token进行签名,使用的算法是HS512,密钥是secret变量
                    .signWith(SignatureAlgorithm.HS512, secret)
                    //compact()方法将JWT Token生成为一个字符串,并将其作为方法的返回值。
                    .compact();
            return token;
        }
    
        /**
         * 从令牌中获取数据声明
         *
         * @param token 令牌
         * @return 数据声明
         */
        private Claims parseToken(String token)
        {
                    //Jwts.parser()方法创建一个JWT Parser对象,用于解析JWT Token
            return Jwts.parser()
                    //setSigningKey(secret)方法设置解析Token时所需的签名密钥,密钥是secret变量。
                    .setSigningKey(secret)
                    //parseClaimsJws(token)方法对传入的JWT Token进行解析。这里的token参数是要解析的JWT Token字符串。
                    .parseClaimsJws(token)
                    //getBody()方法获取解析后的Token内容,返回的是一个Claims对象,包含了Token中的payload部分的键值对信息。
                    .getBody();
        }
    
        /**
         * 从令牌中获取用户名
         *
         * @param token 令牌
         * @return 用户名
         */
        public String getUsernameFromToken(String token)
        {
            //parseToken(token)方法解析传入的JWT Token
            Claims claims = parseToken(token);
            return claims.getSubject();
        }
    
        /**
         * 获取请求token
         *
         * @param request
         * @return token
         */
        private String getToken(HttpServletRequest request)
        {
            //获取请求头名称,通常为Authorization。
            String token = request.getHeader(header);
            //判断一个字符串是否为非空串(详见字符串工具类)
            //判断获取到的Token字符串是否非空,并且是否以预定义的Token前缀Constants.TOKEN_PREFIX开头
            if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX))
            {
                //将Token前缀替换为空字符串,只保留真实的Token内容。
                token = token.replace(Constants.TOKEN_PREFIX, "");
            }
            return token;
        }
    
        private String getTokenKey(String uuid)
        {
            //登录用户 redis key
            //public static final String LOGIN_TOKEN_KEY = "login_tokens:";
            return Constants.LOGIN_TOKEN_KEY + uuid;
        }
    }
    

    附加登录用户身份权限(LoginUser)

    package com.muyuan.common.core.domain.model;
    
    import java.util.Collection;
    import java.util.Set;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.userdetails.UserDetails;
    import com.fasterxml.jackson.annotation.JsonIgnore;
    import com.muyuan.common.core.domain.entity.SysUser;
    
    /**
     * 登录用户身份权限
     * 
     * 
     */
    public class LoginUser implements UserDetails
    {
        private static final long serialVersionUID = 1L;
    
        /**
         * 用户唯一标识
         */
        private String token;
    
        /**
         * 登录时间
         */
        private Long loginTime;
    
        /**
         * 过期时间
         */
        private Long expireTime;
    
        /**
         * 登录IP地址
         */
        private String ipaddr;
    
        /**
         * 登录地点
         */
        private String loginLocation;
    
        /**
         * 浏览器类型
         */
        private String browser;
    
        /**
         * 操作系统
         */
        private String os;
    
        /**
         * 权限列表
         */
        private Set permissions;
    
        /**
         * 用户信息
         */
        private SysUser user;
    
        public String getToken()
        {
            return token;
        }
    
        public void setToken(String token)
        {
            this.token = token;
        }
    
        public LoginUser()
        {
        }
    
        public LoginUser(SysUser user, Set permissions)
        {
            this.user = user;
            this.permissions = permissions;
        }
    
        @JsonIgnore
        @Override
        public String getPassword()
        {
            return user.getPassword();
        }
    
        @Override
        public String getUsername()
        {
            return user.getUserName();
        }
    
        /**
         * 账户是否未过期,过期无法验证
         */
        @JsonIgnore
        @Override
        public boolean isAccountNonExpired()
        {
            return true;
        }
    
        /**
         * 指定用户是否解锁,锁定的用户无法进行身份验证
         * 
         * @return
         */
        @JsonIgnore
        @Override
        public boolean isAccountNonLocked()
        {
            return true;
        }
    
        /**
         * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
         * 
         * @return
         */
        @JsonIgnore
        @Override
        public boolean isCredentialsNonExpired()
        {
            return true;
        }
    
        /**
         * 是否可用 ,禁用的用户不能身份验证
         * 
         * @return
         */
        @JsonIgnore
        @Override
        public boolean isEnabled()
        {
            return true;
        }
    
        public Long getLoginTime()
        {
            return loginTime;
        }
    
        public void setLoginTime(Long loginTime)
        {
            this.loginTime = loginTime;
        }
    
        public String getIpaddr()
        {
            return ipaddr;
        }
    
        public void setIpaddr(String ipaddr)
        {
            this.ipaddr = ipaddr;
        }
    
        public String getLoginLocation()
        {
            return loginLocation;
        }
    
        public void setLoginLocation(String loginLocation)
        {
            this.loginLocation = loginLocation;
        }
    
        public String getBrowser()
        {
            return browser;
        }
    
        public void setBrowser(String browser)
        {
            this.browser = browser;
        }
    
        public String getOs()
        {
            return os;
        }
    
        public void setOs(String os)
        {
            this.os = os;
        }
    
        public Long getExpireTime()
        {
            return expireTime;
        }
    
        public void setExpireTime(Long expireTime)
        {
            this.expireTime = expireTime;
        }
    
        public Set getPermissions()
        {
            return permissions;
        }
    
        public void setPermissions(Set permissions)
        {
            this.permissions = permissions;
        }
    
        public SysUser getUser()
        {
            return user;
        }
    
        public void setUser(SysUser user)
        {
            this.user = user;
        }
    
        @Override
        public Collection getAuthorities()
        {
            return null;
        }
    }
    

你可能感兴趣的:(JAVA代码优化,服务器,运维)