为教学管理系统部门管理,员工管理添加登录校验

在管理系统中,通常需要我们在登录之后再进行数据的操作,我们通常会通过会话跟踪的技术来记录登录状态。

会话技术

 会话跟踪的方案一般分为客户端会话跟踪技术,服务端会话跟踪技术和令牌技术,三种技术的比较如下。

会话跟踪方案对比

 所以我们这里使用令牌技术:JWT令牌技术。

JWT

 jwt技术生成的令牌分为三段,由逗号分隔。第一段和第二段是由Base64进行编码,储存的是明文数据,最后一段保存的是数据签名,需要我们在代码中指定,以达到防止别人修改令牌的目的。

那么如何去使用jwt技术呢,请看下面的代码,我们选择制作一个专门的工具类来进行管理。

package com.itheima.utils;

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 = "itheima";
    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;
    }
}

 这样的话,我们就可以在需要使用到jwt技术的地方调用这个工具类。

package com.itheima.controller;

import com.itheima.pojo.Emp;
import com.itheima.pojo.Result;
import com.itheima.service.EmpService;
import com.itheima.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

/**
 * {@code @Author} 19667
 * {@code @create} 2024/8/1 8:53
 */
@Slf4j
@RestController
public class LoginController {

    @Autowired
    EmpService empService;

    @PostMapping("/login")
    public Result login(@RequestBody Emp emp) {
        log.info("员工登录:{}", emp);
        Emp result = empService.login(emp);
        if(result!=null){
            Map<String, Object> claims = new HashMap<>();
            claims.put("id",result.getId());
            claims.put("name",result.getName());
            claims.put("username",result.getUsername());
            //在此处使用了jwt工具类
            String jwt = JwtUtils.generateJwt(claims);
            return Result.success(jwt);
        }
        return Result.error("用户名或者密码错误");
    }
}

在我们接收到前端传递过来的token之后,我们也可以使用这个工具类来进行验证。

JwtUtils.parseJWT(jwt);

过滤器(Filter)

过滤器的概述

 过滤器是JavaWeb中的组件,我们想在在Springboot的项目中使用它的话需要额外的注解。

Filter快速入门

 下面是一个实现的例子。

package com.itheima.filter;

import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;

import java.io.IOException;

/**
 * {@code @Author} 19667
 * {@code @create} 2024/8/1 11:25
 */
@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {
    //初始化,只会被调用一次
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("init");
    }
    //拦截到请求之后调用,会调用多次
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("doFilter");
        System.out.println("放行前的逻辑");
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("放行后的逻辑");
    }

    //销毁的方法,只会调用一次
    @Override
    public void destroy() {
        System.out.println("destroy");
    }
}

Filter拦截路径

值得我们去注意的是,@WebFilter(urlPatterns = "/*") 注解中括号内的内容就是我们要去拦截的路径,对于注解中的路径,可以参考下面的图片。

 过滤器链

如果我们定义了多个过滤器类,那么就形成了一个过滤器链。

 过滤器之间执行顺序的先后取决于过滤器类的字典序排名。例如在以下这两个过滤器类,DemoFilter会比XbcFilter先执行。

 在使用Filter技术之后,我们就可以给我的管理系统添加上登入认证的功能了。

package com.itheima.filter;

import com.alibaba.fastjson.JSONObject;
import com.itheima.pojo.Result;
import com.itheima.utils.JwtUtils;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.io.IOException;

/**
 * {@code @Author} 19667
 * {@code @create} 2024/8/1 13:32
 */
@Slf4j
@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest res = (HttpServletRequest) servletRequest;
        HttpServletResponse resp = (HttpServletResponse) servletResponse;
        //获取请求的url
        String url = res.getRequestURI();
        log.info("获取到的url:{}",url);
        //判断获取的url是否包含login
        if(url.contains("login")){
            log.info("登录操作,放行");
            //放行
            filterChain.doFilter(servletRequest,servletResponse);
            return;
        }
        //获取请求头中的令牌(token)
        String jwt = res.getHeader("token");
        //如果不存在
        if(!StringUtils.hasLength(jwt)){
            log.info("token为空,未登录");
            Result error = Result.error("NOT_LOGIN");
            //手动转换对象——>json 阿里巴巴 fastjson
            String jsonString = JSONObject.toJSONString(error);
            resp.getWriter().write(jsonString);
            return;
        }
        //如果存在,验证token

        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) {
            e.printStackTrace();
            log.info("验证失败");
            Result error = Result.error("NOT_LOGIN");
            //手动转换对象——>json 阿里巴巴 fastjson
            String jsonString = JSONObject.toJSONString(error);
            resp.getWriter().write(jsonString);
            return;
        }

        //放行
        log.info("令牌合法,放行");
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

在返回数据时,需要返回json格式的对象,在这里我们可以引入阿里巴巴的fastjson依赖

 拦截器(Interceptor)

除了过滤器,我们还可以选择拦截器来实现登录验证的操作。

拦截器的概述

与过滤器不同,拦截器是由spring框架提供的 。

Interceptor快速入门

 使用拦截器需要分别定义两个类,一个类实现HandlerInterceptor接口,并在类名之前加上@Componet注解把它交给ioc容器去管理。另一个类加上@Configuration注解,并在此类中配置拦截器。

package com.itheima.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.itheima.pojo.Result;
import com.itheima.utils.JwtUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

/**
 * {@code @Author} 19667
 * {@code @create} 2024/8/1 14:09
 */
@Slf4j
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
    //目标资源方法运行前运行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //获取请求的url
        String url = request.getRequestURI();
        log.info("获取到的url:{}",url);
        //判断获取的url是否包含login
        if(url.contains("login")){
            log.info("登录操作,放行");
            //放行
            return true;
        }
        //获取请求头中的令牌(token)
        String jwt = request.getHeader("token");
        //如果不存在
        if(!StringUtils.hasLength(jwt)){
            log.info("token为空,未登录");
            Result error = Result.error("NOT_LOGIN");
            //手动转换对象——>json 阿里巴巴 fastjson
            String jsonString = JSONObject.toJSONString(error);
            response.getWriter().write(jsonString);
            return false;
        }
        //如果存在,验证token

        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) {
            e.printStackTrace();
            log.info("验证失败");
            Result error = Result.error("NOT_LOGIN");
            //手动转换对象——>json 阿里巴巴 fastjson
            String jsonString = JSONObject.toJSONString(error);
            response.getWriter().write(jsonString);
            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...");
    }
}
package com.itheima.config;

import com.itheima.filter.LoginCheckFilter;
import com.itheima.interceptor.LoginCheckInterceptor;
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;

/**
 * {@code @Author} 19667
 * {@code @create} 2024/8/1 14:13
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    LoginCheckInterceptor loginCheckInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**").excludePathPatterns("/login");
    }
}

Interceptor拦截路径

Filter和Interceptor

 区别

 执行顺序

 全局异常处理器

 

package com.itheima.excepution;

import com.itheima.pojo.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * {@code @Author} 19667
 * {@code @create} 2024/8/1 14:36
 */
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    public Result ex(Exception ex){
        ex.printStackTrace();
        return Result.error("对不起,操作失败,请联系管理员");
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值