springboot项目使用jwt+token实现登录

首先我们为什么要用token呢?

session和token都是用来保持会话,功能相同;

session用于临时保存用户信息,session存储在服务端,生命周期跟随服务器状态而变化。
token比session更安全。token不存在于服务端,可跨域调用接口信息。token的信息是加密且有实效性的,每次请求过来都需要重新验证

接下来我们直接展示实现代码:

pom文件需要先引入jwt依赖:

        
            com.auth0
            java-jwt
            3.3.0
        

然后是加密的用户信息实体类:

import com.alibaba.fastjson.annotation.JSONField;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;

/**
 * (TahLoginUser)实体类
 *
 * @author makejava
 * @since 2021-12-14 15:09:14
 */
@Data
public class TahLoginUser implements Serializable {

    private static final long serialVersionUID = 833285830459008034L;
    
    private Integer id;

    /**
    * 登录名(不能重复)
    */
    @ApiModelProperty(value = "登录名(不能重复)",required = true)
    private String userName;
    /**
    * 密码
    */
    @ApiModelProperty(value = "密码",required = true)
    private String password;
    /**
    * 钉钉id
    */
    @ApiModelProperty(value = "钉钉id",required = true)
    private String userId;
    /**
    * 真实姓名
    */
    @ApiModelProperty("真实姓名")
    private String realName;
    /**
    * 电话
    */
    @ApiModelProperty("电话")
    private String mobile;
    /**
    * 邮箱
    */
    @ApiModelProperty("邮箱")
    private String email;

}

接下来是jwt加密工具类:

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.google.gson.Gson;
import com.haileer.dd.dingdingserver.entity.TahLoginUser;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;

public class JwtUtil {

    final static String SECRET = "jwtToken";//私钥
    final static Gson gson = new Gson();
    final static long TOKEN_EXP = 60 * 60 * 24 * 30;//过期时间 三十天
//    final static long TOKEN_EXP = 240 ;//过期时间,测试使用

    public static String createJwt(TahLoginUser tahLoginUser) throws UnsupportedEncodingException {
        Algorithm al = Algorithm.HMAC256(SECRET);
        Instant instant = LocalDateTime.now().plusSeconds(TOKEN_EXP).atZone(ZoneId.systemDefault()).toInstant();
        Date expire = Date.from(instant);
        Gson gson = new Gson();
        String s = gson.toJson(tahLoginUser);
        String token = JWT.create()
                .withSubject("userInfo")
                .withClaim("user", s)
                .withExpiresAt(expire)
                .sign(al);
        return token;
    }

    /**
     * @Param: 传入token
     * @return:
     */
    public static boolean verify(String token) throws UnsupportedEncodingException {
        try {
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            JWTVerifier verifier = JWT.require(algorithm).build();
            DecodedJWT jwt = verifier.verify(token);
            if (jwt.getExpiresAt().before(new Date())) {
                System.out.println("token已过期11");
                return false;
            }
        } catch (Exception e) {
            System.out.println("token已过期22");
            return false;
        }
        return true;
    }

    /**
     * 获取用户信息
     *
     * @param request
     * @return
     */
    public static TahLoginUser getUserIdByToken(HttpServletRequest request) throws UnsupportedEncodingException {
        String header = request.getHeader("Authorization");
        String token = header.substring(7);
        Algorithm algorithm = Algorithm.HMAC256(SECRET);
        JWTVerifier verifier = JWT.require(algorithm).build();
        DecodedJWT jwt = verifier.verify(token);
        Claim claim = jwt.getClaim("user");
        String json = claim.asString();
        TahLoginUser tbLoginUser = gson.fromJson(json, TahLoginUser.class);
        return tbLoginUser;
    }


}

封装用户信息工具类:


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * 

* Title: SecurityUserHolder.java *

* *

* Description: SpringSecurity用户获取工具类,该类的静态方法可以直接获取已经登录的用户信息 *

* * @author honghu * @version honghu_b2b2c 8.0 * @date 2014-4-24 */ public class SecurityUserHolder { @SuppressWarnings("unused") private static Logger logger = LoggerFactory.getLogger(SecurityUserHolder.class); public static String prefix = "user_token"; //新增头部参数 private static String outfallTypePrefix = "outfall_type"; public static Integer getCurrentUserId() { Integer userId = null; if (RequestContextHolder.getRequestAttributes() != null) { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); try { userId = (Integer) request.getSession().getAttribute(prefix); } catch (Exception e) { request.getSession().removeAttribute(prefix); return null; } } return userId; } public static Integer getOutfallType() { Integer outfallType = null; if (RequestContextHolder.getRequestAttributes() != null) { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); try { outfallType = (Integer) request.getSession().getAttribute(outfallTypePrefix); } catch (Exception e) { request.getSession().removeAttribute(outfallTypePrefix); return null; } } return outfallType; } public static void setCurrentUserId(Integer userId) { if (RequestContextHolder.getRequestAttributes() != null) { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); request.getSession().setAttribute(prefix, userId); } } public static void setOutfallType(Integer outfallType) { if (RequestContextHolder.getRequestAttributes() != null) { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); request.getSession().setAttribute(outfallTypePrefix, outfallType); } } public static void removeCurrentUser() { if (RequestContextHolder.getRequestAttributes() != null) { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); request.getSession().setAttribute(prefix, null); request.getSession().removeAttribute(prefix); } } public static void removeOutfallType() { if (RequestContextHolder.getRequestAttributes() != null) { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); request.getSession().setAttribute(outfallTypePrefix, null); request.getSession().removeAttribute(outfallTypePrefix); } } }

配置拦截器以及token解析拦截类:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 拦截器链配置
 *
 * @author wsj
 * @date 2019/08/13
 */
@Configuration
public class InteceptorChainConfig implements WebMvcConfigurer {

    @Bean
    public HandlerInterceptor getMyInterceptor() {
        /**
         * bean注解提前加载  解决@Autowired为空的情况
         */
        return new AuthenticationInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //请求参数转换  过滤登录导出图片路径
        registry.addInterceptor(getMyInterceptor()).addPathPatterns("/**").excludePathPatterns(
                "/login/login",//登录接口不拦截 ,其他不需要拦截的接口也在此配置
                "/login/webLogin",
                "/login-user/web-login",
                "/swagger-resources/**",//swagger相关请求路径也不需要拦截
                "/webjars/**",
                "/v2/**",
                "/swagger-ui.html/**",
                "/doc.html", "/doc.html/**");
    }


    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Configuration
public class AuthenticationInterceptor implements HandlerInterceptor {

    @Autowired
    ApplicationArguments applicationArguments;

    @Autowired
    TahLoginUserService loginUserService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if ("OPTIONS".equals(request.getMethod())) {//这里通过判断请求的方法,判断此次是否是预检请求,如果是,立即返回一个204状态吗,标示,允许跨域;预检后,正式请求,这个方法参数就是我们设置的post了
            response.setStatus(HttpStatus.NO_CONTENT.value()); //HttpStatus.SC_NO_CONTENT = 204
            response.setHeader("Access-Control-Allow-Methods", "POST, GET, DELETE, OPTIONS, DELETE");//当判定为预检请求后,设定允许请求的方法
            response.setHeader("Access-Control-Allow-Headers", "Content-Type, x-requested-with, Token"); //当判定为预检请求后,设定允许请求的头部类型
            response.addHeader("Access-Control-Max-Age", "1");
            return true;
        }

        //过滤静态资源图片
        String servletUrl = request.getServletPath();
        if (servletUrl != null && ("/favicon.ico".equals(servletUrl) || "/error".equals(servletUrl))) {
            return true;
        }

        ResultDto resultDto = new ResultDto<>();
        // 从 http 请求头中取出 token
        String header = request.getHeader("Authorization");
        if (StringUtils.isEmpty(header)) {
            SecurityUserHolder.removeCurrentUser();
            resultDto.setError(401, "用户未登录");
            System.out.println("未登录url="+servletUrl);
            throw new CustomException(resultDto);
        }

        String userId = "";
        if (!StringUtils.isEmpty(header)) {
            if (!header.startsWith("Bearer")) {
                SecurityUserHolder.removeCurrentUser();
                resultDto.setError(401, "token错误");
                throw new CustomException(resultDto);
            }
            String token = header.substring(7);
            if (null == header || header.trim().equals("")) {
                SecurityUserHolder.removeCurrentUser();
                resultDto.setError(401, "请求头中没有token");
                throw new CustomException(resultDto);
            }
            boolean verity = JwtUtil.verify(token);
            if (!verity) {
                SecurityUserHolder.removeCurrentUser();
                resultDto.setError(401, "token已过期");
                throw new CustomException(resultDto);
            }
            //拿到userid
            userId = JwtUtil.getUserIdByToken(request).getUserId();
        }

        TahLoginUser user = loginUserService.queryByUserId(userId);
        if (user == null) {
            SecurityUserHolder.removeCurrentUser();
            resultDto.setError(401, "用户不存在");
            throw new CustomException(resultDto);
        }
        //存入userId
        SecurityUserHolder.setCurrentUserId(userId);
        return true;
    }


    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}

}

所用到的自定义CustomException文件:

/**
 * 自定义异常
 *
 * @author ruoyi
 */
public class CustomException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    private Integer code;

    private String message;

    public CustomException(ResultDto resultDto) {
        this.message = resultDto.getMessage();
        this.code = resultDto.getCode();
    }

    @Override
    public String getMessage() {
        return message;
    }

    public Integer getCode() {
        return code;
    }
    
}

接下来展示登录接口:


    @PostMapping("/web-login")
    @ApiOperation("web端登录")
    public String weblogin1(@RequestParam String username, @RequestParam String password) {
        try {
            if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
                return "用户名或密码不能为空";
            }
            TahLoginUser tbLoginUser = new TahLoginUser();
            tbLoginUser.setUserName(username);
            tbLoginUser.setPassword(password);
            List list = loginUserService.queryUserInfo(tbLoginUser);
            if (list == null || list.isEmpty()) {
                return "该用戶不存在";
            }
            if (!list.get(0).getPassword().equals(password)) {
                return "密码不正确";
            }
            TahLoginUser user = list.get(0);
            //加密用户信息
            String token = JwtUtil.createJwt(user);
            return token;
        } catch (Exception e) {
            return e.getMessage();
        }
    }

从登录接口获取的token,存储在客户端, 客户端下次发起请求时放在header中Authorization中,

AuthenticationInterceptor拦截器直接解析,解析通过后放行,否则抛出异常。

到此token登录代码部分就全部完成了,下次见!

你可能感兴趣的:(token登录,spring,服务器,运维)