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)

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<String, Object> 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<String, Object> 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<String> permissions;
 
    /**
     * 用户信息
     */
    private SysUser user;
 
    public String getToken()
    {
        return token;
    }
 
    public void setToken(String token)
    {
        this.token = token;
    }
 
    public LoginUser()
    {
    }
 
    public LoginUser(SysUser user, Set<String> 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<String> getPermissions()
    {
        return permissions;
    }
 
    public void setPermissions(Set<String> permissions)
    {
        this.permissions = permissions;
    }
 
    public SysUser getUser()
    {
        return user;
    }
 
    public void setUser(SysUser user)
    {
        this.user = user;
    }
 
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities()
    {
        return null;
    }
}

Redis操作工具类(RedisCache)

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
public class RedisCache {
    @Autowired
    public RedisTemplate redisTemplate;

/**
 * 缓存基本的对象,Integer、String、实体类等
 *
 * @param key   缓存的键值
 * @param value 缓存的值
 */
public <T> void setCacheObject(final String key, final T value) {
    redisTemplate.opsForValue().set(key, value);
}

/**
 * 缓存基本的对象,Integer、String、实体类等
 *
 * @param key      缓存的键值
 * @param value    缓存的值
 * @param timeout  时间
 * @param timeUnit 时间颗粒度
 */
public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {
    redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
}

/**
 * 设置有效时间
 *
 * @param key     Redis键
 * @param timeout 超时时间
 * @return true=设置成功;false=设置失败
 */
public boolean expire(final String key, final long timeout) {
    return expire(key, timeout, TimeUnit.SECONDS);
}

/**
 * 设置有效时间
 *
 * @param key     Redis键
 * @param timeout 超时时间
 * @param unit    时间单位
 * @return true=设置成功;false=设置失败
 */
public boolean expire(final String key, final long timeout, final TimeUnit unit) {
    return redisTemplate.expire(key, timeout, unit);
}

/**
 * 判断 key是否存在。
 *
 * @param key 缓存键
 * @return true 存在  false 不存在
 */
 public Boolean hasKey(String key)  {
    return redisTemplate.hasKey(key);
}

/**
 * 获得缓存的基本对象。
 *
 * @param key 缓存键值
 * @return 缓存键值对应的数据
 */
public <T> T getCacheObject(final String key) {
    ValueOperations<String, T> operation = redisTemplate.opsForValue();
    return operation.get(key);
}


/**
 * 删除单个对象
 *
 * @param key
 */
public boolean deleteObject(final String key) {
    return redisTemplate.delete(key);
}

/**
 * 删除集合对象
 *
 * @param collection 多个对象
 * @return
 */
public long deleteObject(final Collection collection) {
    return redisTemplate.delete(collection);
}

/**
 * 缓存List数据
 *
 * @param key      缓存的键值
 * @param dataList 待缓存的List数据
 * @return 缓存的对象
 */
public <T> long setCacheList(final String key, final List<T> dataList) {
    Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
    return count == null ? 0 : count;
}

/**
 * 获得缓存的list对象
 *
 * @param key 缓存的键值
 * @return 缓存键值对应的数据
 */
public <T> List<T> getCacheList(final String key) {
    return redisTemplate.opsForList().range(key, 0, -1);
}

/**
 * 缓存Set
 *
 * @param key     缓存键值
 * @param dataSet 缓存的数据
 * @return 缓存数据的对象
 */
public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) {
    BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
    Iterator<T> it = dataSet.iterator();
    while (it.hasNext()) {
        setOperation.add(it.next());
    }
    return setOperation;
}

/**
 * 获得缓存的set
 *
 * @param key
 * @return
 */
public <T> Set<T> getCacheSet(final String key) {
    return redisTemplate.opsForSet().members(key);
}

/**
 * 缓存Map
 *
 * @param key
 * @param dataMap
 */
public <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
    if (dataMap != null) {
        redisTemplate.opsForHash().putAll(key, dataMap);
    }
}

/**
 * 获得缓存的Map
 *
 * @param key
 * @return
 */
public <T> Map<String, T> getCacheMap(final String key) {
    return redisTemplate.opsForHash().entries(key);
}

/**
 * 往Hash中存入数据
 *
 * @param key   Redis键
 * @param hKey  Hash键
 * @param value 值
 */
public <T> void setCacheMapValue(final String key, final String hKey, final T value) {
    redisTemplate.opsForHash().put(key, hKey, value);
}

/**
 * 获取Hash中的数据
 *
 * @param key  Redis键
 * @param hKey Hash键
 * @return Hash中的对象
 */
public <T> T getCacheMapValue(final String key, final String hKey) {
    HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
    return opsForHash.get(key, hKey);
}

/**
 * 删除Hash中的数据
 *
 * @param key
 * @param hkey
 */
public void delCacheMapValue(final String key, final String hkey) {
    HashOperations hashOperations = redisTemplate.opsForHash();
    hashOperations.delete(key, hkey);
}

/**
 * 获取多个Hash中的数据
 *
 * @param key   Redis键
 * @param hKeys Hash键集合
 * @return Hash对象集合
 */
public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) {
    return redisTemplate.opsForHash().multiGet(key, hKeys);
}

/**
 * 获得缓存的基本对象列表
 *
 * @param pattern 字符串前缀
 * @return 对象列表
 */
public Collection<String> keys(final String pattern) {
    return redisTemplate.keys(pattern);
    }
}

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Node.js中,通常使用token来对用户进行身份验证和授权。引用是一个示例代码,它演示了如何创建一个用于生成和验证token的类。该类名为Jwt,它引入了一些必要的模块,如fs、path和jsonwebtoken。该类具有两个方法:generateToken用于生成token,verifyToken用于验证token。生成token时,需要传入数据和有效期,同时使用私钥对数据进行签名;验证token时,需要传入token并使用公钥进行验证。如果验证通过,则返回token中的数据,否则返回错误信息。 在示例代码中,展示了如何在用户登录成功后生成对应的token并返回给客户端。这段代码引入了JwtUtil工具类和AesUtil工具类。在登录接口中,首先查询用户名对应的用户信息,然后使用AesUtil进行密码解密,将解密后的密码与用户输入的密码进行比较。如果匹配成功,则生成对应的token,并将其返回给客户端。 为了保护需要验证身份信息的请求接口,示例代码中引入了中间件函数。该函数对所有需要验证身份的请求进行拦截,并校验token的合法性。如果token校验通过,则继续执行下一个中间件或路由处理程序;否则,返回登录信息不正确的错误提示。 综上所述,可以使用示例代码中的Jwt类来生成和验证token,然后在登录接口中生成对应的token并返回给客户端,同时使用中间件函数来拦截需要验证身份信息的请求并校验token的合法性。这样可以实现Node.js中的token验证机制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值