Java springboot实现JWT登录-附源码

通过jwt官网可以看到,jwt形成的编码是由声明、数据、密钥等部分组成,将登录的用户数据存储到map中,进行编码生成,

jwt生成和解析工具类

java 复制代码
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

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

public class JWTUtils {

    private static String signKey = "密钥自定义";
    //过期时间
    private static Long expire = 1000 * 3600 * 24 * 7L;

    //生成jwt
    public static String generateJwt(Map<String,Object> map){
        return Jwts.builder().addClaims(map)//设置声明(有效数据)
                .signWith(SignatureAlgorithm.HS256,signKey)//设置(使用的算法规则,密钥-自己定义的-不能少于四位)
                .setExpiration(new Date(System.currentTimeMillis()+expire))//设置过期时间
                .compact();//设置生效
    }

    //解析jwt  Claims-map结构 可以使用get方法去取值,返回的是用户信息,表示是登录状态的
    public static Claims parseJwt(String jwt){
        return Jwts.parser()
                .setSigningKey(signKey) //密钥
                .parseClaimsJws(jwt) //调用jws 指定生成的令牌
                .getBody();
    }
}

登录业务实现

Controller

这里将业务处理放到了service中,controller简单进行了调用,通过前端传递的用户名和密码,将其封装在员工表Emp中

java 复制代码
@RestController
public class LoginController {

    @Autowired
    private EmpService empService;

    @PostMapping("/login")
    public Result login(@RequestBody Emp emp){
        return empService.login(emp);
    }

}

service

java 复制代码
//接口:
Result login(Emp emp);


//实现类:
@Override
    public Result login(Emp emp) {
    	//通过mapper查询数据库中是否存在用户名和密码一致的用户
        Emp em = empMapper.selByNameAndPass(emp);
        //如果查询到,表示用户存在
        if (em != null){
        	//通过新建map,将需要放到token里面的数据进行put
            Map<String, Object> map = new HashMap<>();
            map.put("id",em.getId());
            map.put("username",em.getUsername());
            //调用工具类,进行token生成
            String token = JwtUtils.generateJwt(map);
            //将生成的token返回给前端
            return Result.success(token);
        }else {
        //没查询到,表示信息有误
            throw new LoginException("用户名或密码错误");
        }

    }

mapper

java 复制代码
//接口:
Emp selByNameAndPass(Emp emp);
//xml:
<select id="selByNameAndPass" resultType="cn.emp.domain.Emp">
        select * from emp where username = #{username} and password = #{password}
    </select>

拦截器

通过拦截器实现,没有登录信息token符合要求时,跳转到登录页进行登录

java 复制代码
import cn.wolfcode.domain.Result;
import cn.wolfcode.util.JwtUtils;
import com.alibaba.fastjson.JSONObject;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        try {
            String token = request.getHeader("token");
            JwtUtils.parseJwt(token);
            return true;
        } catch (Exception e) {
            response.setContentType("application:json/charset=utf-8");
            Result result = Result.error("NOT_LOGIN");
            String jsonString = JSONObject.toJSONString(result);
            response.getWriter().write(jsonString);
            return false;
        }
    }
}

设置拦截的路径和放行的路径

java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

//将当前类转变为配置类
@Configuration
public class WebConfig implements WebMvcConfigurer {

	//拿到定义的拦截器
    @Autowired
    private LoginInterceptor loginInterceptor;

	//拦截器注册表
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    	/*
         * 拦截器如果拦截的路径是 /* 只能拦截一级路径
         * '/**' 可以拦截多级路径
         * excludePathPatterns 设置放行路径
         */
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/login");
    }
}

过滤器

java 复制代码
import cn.wolfcode.domain.Result;
import cn.wolfcode.util.JWTUtils;
import com.alibaba.fastjson.JSONObject;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

//给过滤器配置要过滤的路径
@WebFilter("/*")
public class DemoFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        //认证逻辑
        HttpServletRequest req = (HttpServletRequest)request;
        HttpServletResponse resp = (HttpServletResponse)response;

        //login接口放行,拿到请求资源URI
        String requestURI = req.getRequestURI();
        if (requestURI.contains("/login")){
            chain.doFilter(request,response);
            //结束后续方法执行
            return;
        }
        //获取令牌
        String token = req.getHeader("token");
        try {
            //进行解析 有异常不放行,没有异常放行
            JWTUtils.parseJwt(token);
            //负责放行的语句
            chain.doFilter(request,response);
        } catch (IOException e) {
            //原生响应
            resp.setContentType("application/json;charset=utf-8");
            Result result = Result.error("NOT_LOGIN");
            //将result对象手动转换为json(通过添加fastjson依赖实现)
            String jsonString = JSONObject.toJSONString(result);
            resp.getWriter().write(jsonString);
        }
    }
}
复制代码
过滤器需要在启动类上添加注解,使过滤器生效
//使filter注解生效
@ServletComponentScan

过滤器和拦截器选择一个进行实现就可以