springboot的jwt实现登录态相关工具,流程汇总

1.首先是jwt的相关maven依赖

        
        
            io.jsonwebtoken
            jjwt
            0.9.1
        
        
        
            org.springframework.boot
            spring-boot-starter-security
            2.2.4.RELEASE
        

2.JwtTokenUtil.java

package 

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JwtToken生成的工具类
 * JWT token的格式:header.payload.signature
 * header的格式(算法、token的类型):
 * {"alg": "HS512","typ": "JWT"}
 * payload的格式(用户名、创建时间、生成时间):
 * {"sub":"wang","created":1489079981393,"exp":1489684781}
 * signature的生成算法:
 * HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
 */
@Component
public class JwtTokenUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenUtil.class);
    private static final String CLAIM_KEY_USERNAME = "sub";
    // 用于获取token创建时间
    private static final String CLAIM_KEY_CREATED = "created";

    @Value("${jwt.secret}")
    private String secret;
    @Value("${jwt.expiration}")
    private Long expiration;

    /**
     * 根据负责生成JWT的token
     */
    private String generateToken(Map claims) {
        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(generateExpirationDate())
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    /**
     * 从token中获取JWT中的负载
     */
    private Claims getClaimsFromToken(String token) {
        Claims claims = null;
        try {
            claims = Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            LOGGER.info("JWT格式验证失败:{}", token);
        }
        return claims;
    }

    /**
     * 生成token的过期时间
     */
    private Date generateExpirationDate() {
        return new Date(System.currentTimeMillis() + expiration * 1000);
    }

    /**
     * 获取创建时间
     */
    public Date getCreateDateFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return (Date) claims.get("created", Date.class);
    }

    /**
     * 从token中获取登录用户名
     */
    public String getUserNameFromToken(String token) {
        String username;
        try {
            Claims claims = getClaimsFromToken(token);
            username = claims.getSubject();
        } catch (Exception e) {
            username = null;
        }
        return username;
    }

    /**
     * 验证token是否还有效
     *
     * @param token       客户端传入的token
     * @param userDetails 从数据库中查询出来的用户信息
     */
    public boolean validateToken(String token, UserDetails userDetails) {
        String username = getUserNameFromToken(token);
        return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
    }

    /**
     * 判断token是否已经失效
     */
    private boolean isTokenExpired(String token) {
        Date expiredDate = getExpiredDateFromToken(token);
        return expiredDate.before(DateUtil.localTime());
    }

    /**
     * 从token中获取过期时间
     */
    public Date getExpiredDateFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims.getExpiration();
    }

    /**
     * 根据用户信息生成token
     */
    public String generateToken(UserDetails userDetails) {
        Map claims = new HashMap<>();
        claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
        claims.put(CLAIM_KEY_CREATED, DateUtil.localTime());
        return generateToken(claims);
    }

    /**
     * 判断token是否可以被刷新
     */
    public boolean canRefresh(String token) {
        return !isTokenExpired(token);
    }

    /**
     * 刷新token
     */
    public String refreshToken(String token) {
        Claims claims = getClaimsFromToken(token);
        claims.put(CLAIM_KEY_CREATED, DateUtil.localTime());
        return generateToken(claims);
    }
}

3.涉及到的用户信息实体类-UserAuthDetails.java

package ;

import com.common.Constants;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;

/**
 * 用户权限管理
 */
public class UserAuthDetails implements UserDetails {
    private Integer userId;
    private String username;
    private String password;
    private Integer status;

    public UserAuthDetails(Integer userId, String username, String password, Integer status) {
        this.userId = userId;
        this.username = username;
        this.password = password;
        this.status = status;
    }

    public Integer getUserId() {
        return userId;
    }

    @Override
    public Collection getAuthorities() {
        return null;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return status != Constants.USER_STATUS_DELETED;
    }

    @Override
    public boolean isAccountNonLocked() {
        return status != Constants.USER_STATUS_FORBIDDEN;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return status != Constants.USER_STATUS_INVALID;
    }

    @Override
    public boolean isEnabled() {
        return status == Constants.USER_STATUS_VALID;
    }
}

4.JWT登录授权过滤器 JwtAuthenticationTokenFilter.java

package com.config;

import com.bo.UserAuthDetails;
import com.component.UserDataContextHolder;
import com.util.JwtTokenUtil;
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.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * JWT登录授权过滤器
 */
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    private static final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class);
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Value("${jwt.tokenHeader}")
    private String tokenHeader;
    @Value("${jwt.tokenHead}")
    private String tokenHead;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        String authToken;
        String authHeader = request.getHeader(this.tokenHeader);
        if (authHeader != null && authHeader.startsWith(this.tokenHead)) {
            // The part after "Bearer "
            authToken = authHeader.substring(this.tokenHead.length());
        } else {
            authToken = request.getParameter("jwtToken");
        }
        //当token为空或格式错误时 直接放行
        if (authToken == null) {
            chain.doFilter(request, response);
            return;
        }

        //获取用户信息
        UsernamePasswordAuthenticationToken authenticationToken = getAuthentication(authToken);
        if(authenticationToken!=null){
            UserAuthDetails userDetails = (UserAuthDetails) authenticationToken.getPrincipal();
            UserDataContextHolder.setUserId(userDetails.getUserId());
            UserDataContextHolder.setUsername(userDetails.getUsername());
        }

        //将authentication信息放到session里
        //如果token有值但校验不通过,则需要清空上下文
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        chain.doFilter(request, response);
    }

    /**
     * token中获取用户信息
     */
    private UsernamePasswordAuthenticationToken getAuthentication(String authToken) {
        String username = jwtTokenUtil.getUserNameFromToken(authToken);
        LOGGER.info("username authToken:{}", username);

        if (username != null) {
            UserAuthDetails userDetails = null;
            if (SecurityContextHolder.getContext().getAuthentication() == null) {
                userDetails = (UserAuthDetails) this.userDetailsService.loadUserByUsername(username);
            } else {
                UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken)SecurityContextHolder.getContext().getAuthentication();
                userDetails = (UserAuthDetails) authentication.getPrincipal();
                if (!username.equals(userDetails.getUsername())) {
                    userDetails = (UserAuthDetails) this.userDetailsService.loadUserByUsername(username);
                }
            }

            //authToken是否过期,用户是否是同一个人
            if (jwtTokenUtil.validateToken(authToken, userDetails)) {
                return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            }
        }

        return null;
    }
}

5.保存存储用户session信息的实体类:UserDataContextHolder.java

package com.component;

/**
 * 用于存储用户session信息
 */
public class UserDataContextHolder {
    private static ThreadLocal userId = new ThreadLocal<>();
    private static ThreadLocal username = new ThreadLocal<>();
    private static ThreadLocal language = new ThreadLocal<>();
    private static ThreadLocal timezone = new ThreadLocal<>();
    private static ThreadLocal jwtToken = new ThreadLocal<>();
    private static ThreadLocal basePath = new ThreadLocal<>();
    private static ThreadLocal hostname = new ThreadLocal<>();
    private static ThreadLocal device = new ThreadLocal<>();

    public static Integer getUserId() {
        return userId.get();
    }

    public static void setUserId(Integer userId) {
        UserDataContextHolder.userId.set(userId);
    }

    public static String getUsername() {
        return username.get();
    }

    public static void setUsername(String username) {
        UserDataContextHolder.username.set(username);
    }

    public static String getLanguage() {
        return language.get();
    }

    public static void setLanguage(String language) {
        UserDataContextHolder.language.set(language);
    }

    public static String getTimezone() {
        return timezone.get();
    }

    public static void setTimezone(String timezone) {
        UserDataContextHolder.timezone.set(timezone);
    }

    public static String getJwtToken() {
        return jwtToken.get();
    }

    public static void setJwtToken(String jwtToken) {
        UserDataContextHolder.jwtToken.set(jwtToken);
    }

    public static String getBasePath() {
        return basePath.get();
    }

    public static void setBasePath(String basePath) {
        UserDataContextHolder.basePath.set(basePath);
    }

    public static String getHostname() {
        return hostname.get();
    }

    public static void setHostname(String hostname) {
        UserDataContextHolder.hostname.set(hostname);
    }

    public static String getDevice() {
        return device.get();
    }

    public static void setDevice(String hostname) {
        UserDataContextHolder.device.set(hostname);
    }
}

6.jwt的yml文件配置

jwt:
  tokenHeader: Authorization #JWT存储的请求头
  secret: z26ER^SLE45G*SEFz$U!P4T #JWT加解密使用的密钥
  expiration: 604800 #JWT的超期限时间(7*24*60*60)
  tokenHead: Bearer  #JWT负载中拿到开头

7.对应前端的request请求效果

springboot的jwt实现登录态相关工具,流程汇总_第1张图片

你可能感兴趣的:(登陆)