• 作者:老汪软件技巧
  • 发表时间:2024-10-10 17:03
  • 浏览量:

前言:

在现代的Web应用开发中,前后端分离架构逐渐成为主流。而在这样架构下,安全性就显得尤为重要,尤其是用户登录验证的实现。JWT(JSON Web Token)凭借其无状态、跨语言支持等特性,成为了非常流行的登录验证方案。今天,我们将详细介绍如何在Spring Boot和Vue组合的项目中,通过JWT和拦截器来实现登录验证功能,确保只有登录成功的用户才能访问相应的后台资源。

具体操作:一、后端:SpringBoot配置jjwt依赖1.1、添加依赖

首先,需要在pom.xml文件中添加 jjwt 和一些其他的依赖


        <dependency>
            <groupId>io.jsonwebtokengroupId>
            <artifactId>jjwtartifactId>
            <version>0.9.1version>
        dependency>

        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>2.0.49version>
        dependency>

1.2、JWT工具类

创建一个JwtUtils工具类,用来生成和解析token

ps:其中这里面保存的密钥和有效时间可以在application.properties中保存,并创建一个类获得这些数据便于调用

public class JwtUtils {
    private static String signKey = "xctest";
    private static Long expire = 43200000L;
    /**
     * 生成JWT令牌
     * @param claims JWT第二部分负载 payload 中存储的内容
     * @return
     */
    public static String generateJwt(Map<String, Object> claims){
        String jwt = Jwts.builder()
                .addClaims(claims)
                .signWith(SignatureAlgorithm.HS256, signKey)
                .setExpiration(new Date(System.currentTimeMillis() + expire))
                .compact();
        return jwt;
    }
    /**
     * 解析JWT令牌
     * @param jwt JWT令牌
     * @return JWT第二部分负载 payload 中存储的内容
     */
    public static Claims parseJWT(String jwt){
        Claims claims = Jwts.parser()
                .setSigningKey(signKey)
                .parseClaimsJws(jwt)
                .getBody();
        return claims;
    }
}

接着可以使用postman进行检验,查看返回的信息是否正确

这里返回了对应的对象信息以及token,测试正确

​编辑

1.3、登录接口

在登录接口中实现对应逻辑,当用户成功登录后生成一个token并返回给前端

@PostMapping("/login")
    public ResponseEntity> login(@RequestBody LoginRequest loginRequest) {
        User user = userService.selectByPhone2(loginRequest.getPhoneNumber());
        if (user != null && userService.checkPassword(loginRequest.getPassword(), user.getUserPassword())) {
            if (user.getUserRole().equals(loginRequest.getRole())){
                //登录成功后生成token
                Map<String, Object> claims = new HashMap<>();
                claims.put("username",user.getUserName());
                claims.put("userphone",user.getUserPhone());
                claims.put("userrole",user.getUserRole());
                String jwt = JwtUtils.generateJwt(claims);
                //向前端返回对象和token信息
                return ResponseEntity.ok(ApiResponse.success("登录成功",Map.of("user", user, "token", jwt)));
            }else {
                return ResponseEntity.status(401).body(ApiResponse.error("角色不匹配"));
            }
        } else {
            return ResponseEntity.status(401).body(ApiResponse.error("登录失败,账号或密码密码错误"));
        }
    }

1.4、JWT拦截器

创建一个拦截器LoginCheckInterceptor继承HandlerInterceptor并重写其中的三个方法,一般只重写preHandle,其他两个方法默认即可

重写preHandle方法时一般是遵循下面过程:

1.获取请求的url路径

2.判断路径是否为登录或者注册界面(如果是就放行,这两个界面一般不需要登录后获取token;如果不是则继续操作对token进行验证),前面两步操作可省略,因为后面也会对路径进行检查

3.获取前端返回的令牌信息

4.验证当前令牌是否存在,根据令牌的长度来判断,为null则说明令牌不存在并向前端返回错误信息(此操作可省略)

5.验证当前令牌是否有效,判断令牌是否过时等,若令牌无效会抛出异常,此时继续向前端返回错误信息

6.放行

如何打造高效课堂__前后端分离登录验证

@Slf4j
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //获取请求的url路径
        String url = request.getRequestURL().toString();
        log.info("获取到的url路径为:{}",url);
        //判断url路径中是否存在login,存在则说明是登录操作放行
        if(url.contains("login")||url.contains("register")){
            log.info("当前是登录操作,放行。。。");
            return true;
        }
        //若是其他操作,则获取令牌并验证
        String jwt =request.getHeader("token");
        //先验证令牌是否存在(判断长度是否为null)
        if (!StringUtils.hasLength(jwt)) {
            log.info("当前token为空,返回未登录信息");
            response.setContentType("application/json; charset=UTF-8");
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);//设置HTTP响应状态码为401,表示未授权
            JSONObject json = new JSONObject();
            json.put("success",false);
            json.put("message","未登录状态(token不存在)");
            response.getWriter().write(json.toString());
            return false;
        }
        //再验证令牌是否正确(看是否抛出异常)
        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) {
            response.setContentType("application/json; charset=UTF-8");
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);//设置HTTP响应状态码为401,表示未授权
            JSONObject json = new JSONObject();
            json.put("success",false);
            json.put("message","未登录状态(token无效)");
            response.getWriter().write(json.toString());
            return false;
        }
        //放行
        log.info("令牌合法,成功登录放行");
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle运行");
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion运行");
    }
}

1.5、注册拦截器

在WebConfig中添加拦截器,确定哪些路径需要被拦截

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**").excludePathPatterns("/login");
    }
}

在postman中进行测试,当不携带token时查询信息,会显示为登录,无法获得信息

​编辑

接着我们在请求头中添加上token再进行查询,就可以得到数据了

​编辑

​编辑

二、前端:Vue实现登录与请求验证2.1、main.js中配置拦截器

在main.js中配置axios后添加上拦截器

// 添加请求拦截器
axios.interceptors.request.use(config => {
    const token = localStorage.getItem('token');
    if (token) {
        config.headers['token'] = token; // 在每个请求中添加token
    }
    return config;
}, error => {
    return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(response => {
    return response;
}, error => {
    console.log(error.response)
    // 检查响应状态
    if (error.response.status === 401) {
        // 如果未授权,则跳转到登录界面
        localStorage.removeItem('token'); // 清除token
        this.$router.push({ name: 'Login' }); // 跳转到登录页面
    }
    return Promise.reject(error);
});

2.2、index.js中添加路由守卫

index.js中指定公开界面和要授权界面

// 添加路由守卫
router.beforeEach((to, from, next) => {
    const token = localStorage.getItem('token');
    const publicPages = ['/login', '/register']; // 公开页面
    const authRequired = !publicPages.includes(to.path); // 需要授权的页面
    if (authRequired && !token) {
        next({ name: 'Login' }); // 如果未登录,重定向到登录页面
    } else {
        next(); // 放行
    }
});

2.3、管理token信息

在登录界面中,成功登录后会保存token信息,退出界面中,退出时会清除token信息,这里不在赘述

知识点:

拦截器:Spring Boot中的拦截器可以用于在请求进入控制器之前进行处理,如验证请求是否携带合法的Token。

Vue中的Axios拦截器:在每次发出HTTP请求时自动携带Token,并在Token失效时跳转到登录页面,提升用户体验。

总结:

通过这篇教程,我们成功实现了一个基于Spring Boot + Vue的登录验证系统,并使用JWT保证了前后端分离项目的安全性。JWT不仅可以简化会话管理,还能提升系统的扩展性。希望大家能根据项目需求灵活应用JWT,打造更加安全的Web应用。

如果你感觉有帮到你的话,就给我一个点赞收藏吧!你的支持是我的最大动力!!!