JavaWeb开发 —— 登录认证校验和异常处理

news2024/11/27 0:40:44

在 JavaWeb开发 —— SpringBootWeb综合案例 中我们通过实例部门管理以及员工管理中的详细操作。而这篇文章我们将会通过综合实例学习登录认证、登录校验以及异常处理的了解和掌握。

目录

一、基本登录功能

二、登录校验

1. 会话技术

1.1  客户端会话跟踪技术Cookie

1.2  服务端会话跟踪技术Session

1.3  令牌技术

2. JWT令牌

2.1  令牌介绍

2.2  生成与校验

2.3  案例:JWT实现跟踪会话(生成令牌)

3. 过滤器Filter

3.1  入门概述

3.2  过滤器详解(执行流程-拦截路径-过滤器链)

3.3   案例:过滤器Filter实现登录校验

4. 拦截器Interceptor

4.1  入门概述

4.2  拦截器详解(拦截路径-执行流程)

4.3  案例:拦截器Interceptor实现登录校验

三、异常处理


一、基本登录功能

//@slf4j
@RestController
public class LoginController {
    @Autowired
    private EmpService empService;
    @PostMapping("/login")
    public Result login(@RequestBody Emp emp){
        //log.info("员工登录:{}",emp)
        Emp e = empService.login(emp);
        return e != null?Result.success():Result.error("用户名或密码错误");

    }
}

public interface EmpService {
    Emp login(Emp emp);
}

@Service
public class EmpServiceImpl implements EmpService {
    @Override
    public Emp login(Emp emp) {
        return empMapper.getByUsernameAndPassword(emp);
    }
}

@Mapper
public interface EmpMapper {
    @Select("select * from emp where username = #{username} and password = #{password}")
    Emp getByUsernameAndPassword(Emp emp);
}

二、登录校验

① 问题:在未登录情况下,我们也可以直接通过地址URL访问部门管理、员工管理等功能。

登录校验指的是我们在服务器端接收到客户端发送的请求后,首先要对请求进行校验,校验用户是否登录。如果已经登录则执行对应的业务操作,否则就不允许执行业务操作,前端响应错误结果并且挑战到登录页面,要求登陆成功再执行业务操作。

② 基本流程:

登录标记
  • 用户登录成功之后,每一次请求中,都可以获取到该标记。(会话技术)
统一拦截
  • 过滤器 Filter
  • 拦截器 lnterceptor

1. 会话技术

① 会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应。
② 会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据
③ 会话跟踪方案:

  • 客户端会话跟踪技术:Cookie
  • 服务端会话跟踪技术:Session
  • 令牌技术

 ④ 会话跟踪方案对比:

1.1  客户端会话跟踪技术Cookie

在客户端第一次发起请求服务器时,可以设置Cookie来存储请求的信息,服务端在在给客户端响应数据时会自动将Cookie响应给浏览器,浏览器接收到Cookie后会自动将值存储在浏览器本地,在后续每一次请求时都会将本地存储的Cookie自动携带到服务器端,接下来服务器端就可以获取到Cookie值并判断其是否存在,如果不存在则说明在这之前客户端并没有访问服务器端,否则已经登录完成,我们就可以基于Cookie在同一次会话的不同请求之间来共享数据。

问题:为什么Cookie会话操作是自动化进行的呢?

  • Cookie是HTTP协议支持的技术。在HTTP协议中提供了响应头Set-Cookie以及请求头Cookie
  • 详细讲解:HTTP 请求报头 Cookie  、  HTTP 响应报头 Set-Cookie
//@slf4j
@RestController
public class SessionController {
    //设置Cookie
    @GetMapping("/c1")
    public Result cookie1(HttpServletResponse response){
        // 设置cookie / 响应cookies
        response.addCookie(new Cookie("login_username", "itheima"));
        return Result.success();
    }

    //获取Cookie
    @GetMapping("/c2")
    public Result cookie2(HttpServletRequest request){
        // 获取所有的cookies
        Cookie[] cookies = request.getCookies();
        for(Cookie cookie : cookies){
            if (cookie.getName().equals("login_username")){
                // 输出name 为 login_username 的 cookies
                System.out.println("login_username" + cookie.getValue());
            }
        }
        return Result.success();
    }
}
优点
  • HTTP协议中支持的技术。
缺点
  • 移动端APP无法使用cookie。
  • 不安全,用户可以自己禁用cookie。
  • cookie不能跨域。

跨域区分三个维度:协议、IP/域名、端口号。

1.2  服务端会话跟踪技术Session

Session存储在服务器端,而Session底层其实就是基于Cookie实现。浏览器再第一次请求服务器端时,我们可以直接在服务器端获取一个会话对象Session,而第一次请求该会话对象Session是不存在的,会自动创建会话对象Session,并且每一个Session对象都有 Id。接下来服务器端响应数据给浏览器时,会将Session的 Id 通过Cookie响应给浏览器,浏览器会自动将其存储在本地。在后续每一次请求中,都会将Cookie的 Id 获取出来并且携带到服务器端,并且根据 Id 在众多Seesion会话中找到当前请求对应会话对象Session。因此我们就可以根据Session会话对象在同一次会话的多次请求之间共享数据。

//@slf4j
@RestController
public class SessionController {
    //往HttpSession中存储值
    @GetMapping("/s1")
    public Result session1(HttpSession session){
        //log.info("HttpSession-s1:{}",session.hashCode());

        // 往Session中存储数据
        session.setAttribute("loginUser", "tom");
        return Result.success();
    }
    //往HttpSession中获取值
    @GetMapping("/s2")
    public Result session2(HttpServletRequest request){
        HttpSession session = request.getSession();
        //log.info("httpSession.s2:{}",session.hashCode());

        // 从sess中获取数据
        Object loginUser = session.getAttribute("loginUser");
        //log.info("loginUser:{}",loginUser);
        return Result.success(loginUser);
    }
}
优点
  • 存储在服务器端,安全。
缺点
  • 服务器集群环境下无法直接使用Session。
  • Cookie的缺点。

1.3  令牌技术

浏览器在发送请求时,如果成功就会生成令牌,就是用户唯一合法身份凭证。接下来再相应数据时,就可以直接将令牌响应给前端,并且在前端接收到令牌后存储起来,这个存储不仅可以存储在Cookie当中也可以存储在其他存储空间当中。接下来再在一次请求当中,都会将令牌携带到服务器端并且校验令牌的有效性,如果是有效的则说明用户已经执行操作,如果是无效的则说明用户还未执行操作。那么此时如果在同一次会话中的多次请求之间我们想要共享数据,就可以将共享数据存储在令牌当中。

优点
  • 支持PC端、移动端。
  • 解决集群环境下的认证问题。
  • 减轻服务器端存储压力。
缺点
  • 需要自己实现。

2. JWT令牌

2.1  令牌介绍

① 简介:JSON Web Token (https://jwt.io/)定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的。

② 组成:

  • 第一部分:Header(头)记录令牌类型、签名算法等。例如:{"alg":"HS256","type":"JWT"}
  • 第二部分:Payload(有效载荷)携带一些自定义信息、默认信息等。例如:{"id":"1","username":"Tom")
  • 第三部分:Signature(签名)防止Token被篡改、确保安全性。将header、payload,并加入指定秘钥,通过指定签名算法计算而来。

 Base64:是一种基于64个可打印字符(A-Z a-z 0-9 + /)来表示二进制数据的编码方式。

③ 场景:登录认证

  1. 登录成功后,生成令牌。
  2. 后续每个请求,都要携带JWT令牌,系统在每次请求处理之前,先校验令牌,通过后,再处理。

2.2  生成与校验

<!--        JWT令牌依赖-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

//测试类
class TliasWebManagementApplicationTests {
    /**
     * 生成jwt
     */
    @Test
    public void testGenJwt(){
        Map<String,Object> claims = new HashMap<>();
        claims.put("id", 1);
        claims.put("name", "tom");
        String jwt = Jwts.builder()
                .signWith(SignatureAlgorithm.HS256,"itheima")  //设置签名算法
                .setClaims(claims)  //设置自定义内容(载荷)
                .setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))  //设置有效期为1h
                .setExpiration(new Date(System.currentTimeMillis()))  //设置有效期为立即有效
                .compact();
        System.out.println(jwt);
    }

    /**
     * 解析jwt
     */
    @Test
    public void testParseJwt(){
        Claims claims = Jwts.parser()
                .setSigningKey("itheima")  //设置签名密钥
                .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidG9tIiwiaWQiOjEsImV4cCI6MTY4MjM5MTQ1OX0.hVBDzQEyYVaHIeVOMZ5NyWk2k6tkM6ngq_8gLWYQZTk")  //传递jwt令牌
                .getBody();  //获取自定义内容
        System.out.println(claims);

    }
}

 当我们将JWT令牌有效时间设置为立即生成时,当我们再次解析时就会报错JWT过期异常。

注意事项:

  • JWT校验时使用的签名秘钥,必须和生成JWT令牌时使用的秘钥是配套的。
  • 如果JWT令牌解析校验时报错,则说明JWT令牌被篡改或失效了,令牌非法。

2.3  案例:JWT实现跟踪会话(生成令牌)

① 思路:

  • 令牌生成:登录成功后,生成JwT令牌,并返回给前端。
  • 令牌校验:在请求到达服务端后,对令牌进行统一拦截、校验。

② 说明:

  • 用户登录成功后,系统会自动下发JWT令牌,然后在后续的每次请求中,都需要在请求头header中携带到服务端,请求头的名称为token ,值为登录时下发的JWT令牌。
  • 如果检测到用户未登录,则会返回固定错误信息。

③ 步骤:

  • 引入JWT令牌操作工具类。
    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令牌,并返回。
    //@slf4j
    @RestController
    public class LoginController {
        @Autowired
        private EmpService empService;
        @PostMapping("/login")
        public Result login(@RequestBody Emp emp){
            //log.info("员工登录:{}",emp)
            Emp e = empService.login(emp);
    
            //登录成功:生成令牌并下发令牌
            if (e != null){
                Map<String, Object> claims = new HashMap<>();
                claims.put("id",e.getId());
                claims.put("name", e.getName());
                claims.put("username", e.getUsername());
    
                //jwt中包含了当前登录的员工信息
                String jwt = JwtUtils.generateJwt(claims);
                return Result.success(jwt);
            }
            //登录失败:返回错误信息
            return Result.error("用户名或密码错误");
    
        }
    }

    通过上述学习我们了解了通过JWT令牌实现用户登录标记,那么接下来我们将了解统一校验JWT令牌,也就是登录成功标记的两种方式:过滤器Filter、拦截器Interceptor

3. 过滤器Filter

3.1  入门概述

① 概念:Filter过滤器,是JavaWeb 三大组件(Servlet、Filter、Listener)之一。

过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能。

过滤器一般完成一些通用的操作,比如:登录校验、统一编码处理、敏感字符处理等。

 ④ Filter快速入门:

void init (FilterConfig filterConfig)初始化方法,Web服务器启动,创建Filter时调用,只调用一次
void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)拦截到请求时,调用该方法,可调用多次
void destroy ()销毁方法,服务器关闭时调用,只调用一次
  1. 定义Filter:定义一个类,实现 Filter接口,并重写其所有方法。
  2. 配置Filter:Filter类上加@WebFilter注解,配置拦截资源的路径。引导类上加@ServletComponentScan开启Servlet组件支持。
@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {

    @Override  // 初始化方法,只调用一次
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("inti 初始化方法执行了");
    }

    @Override  // 每次拦截到请求之后都会调用,调用多次
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("拦截到请求");
        //放行
        filterChain.doFilter(servletRequest, servletResponse);
    }

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

@ServletComponentScan //开启了对Servlet组件的实现
@SpringBootApplication
public class TliasWebManagementApplication {

    public static void main(String[] args) {
        SpringApplication.run(TliasWebManagementApplication.class, args);
    }
}

3.2  过滤器详解(执行流程-拦截路径-过滤器链)

① 执行流程:在拦截到请求后,我们需要通过放行操作访问web资源,而放行就是fFilterChain对象的doFilter方法,在过滤器放行之前我们可以执行放行前逻辑,而在访问完web资源再回到Filter过滤器后同样也可以执行放行后逻辑。

 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("拦截到请求");
        System.out.println("放行前执行逻辑 ...");
        //放行
        filterChain.doFilter(servletRequest, servletResponse);

        System.out.println("放行后执行逻辑 ...");
    }

疑问:

  • 放行后访问对应资源,资源访问完成后,还会回到Filter中吗?
  • 如果回到Filter中,是重新执行还是执行放行后的逻辑呢? 执行放行后逻辑

② 拦截路径:Filter可以根据需求,配置不同的拦截资源路径:

@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {}
拦截路径urlPatterns值含义
拦截具体路径/login只有访问/login路径时,才会被拦截。
目录拦截/emps/*访问/emps下的所有资源,都会被拦截。
拦截所有/*访问所有资源,都会被拦截。

③ 过滤器链

  • 介绍:一个web应用中,可以配置多个过滤器,这多个过滤器就形成了一个过滤器链

@WebFilter(urlPatterns = "/*")
public class AbcFilter implements Filter {
    @Override  // 每次拦截到请求之后都会调用,调用多次
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("Abc拦截到请求 Abc放行前执行逻辑 ...");
        //放行
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("Abc放行后执行逻辑 ...");
    }
}

@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {
    @Override  // 每次拦截到请求之后都会调用,调用多次
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("demo拦截到请求 放行前执行逻辑 ...");
        //放行
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("demo放行后执行逻辑 ...");
    }
}

 此时当我们执行登录操作时:

Postman返回值:

注意:为什么 AbcFilter 要先于 DemoFilter 执行呢?

  • 顺序:注解配置的Filter,优先级是按照过滤器类名(字符串)的自然排序

3.3   案例:过滤器Filter实现登录校验

① 思考:

  • 所有的请求,拦截到了之后,都需要校验令牌吗?  有一个例外,登录请求
  • 拦截到请求后,什么情况下才可以放行,执行业务操作?  有令牌,且令牌校验通过(合法)﹔否则都返回未登录错误结果

② 实现思路:

步骤:

  • 获取请求url。
  • 判断请求url中是否包含login,如果包含,说明是登录操作,放行。
  • 获取请求头中的令牌( token) 。
  • 判断令牌是否存在,如果不存在,返回错误结果(未登录)。
  • 解析token,如果解析失败,返回错误结果(未登录)。
  • 放行。
@Slf4j
@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //首先进行强转
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse resp = (HttpServletResponse) servletResponse;

        //1.获取请求url。
        String url = req.getRequestURL().toString();
        log.info("请求的url:{}",url)

        //2.判断请求url中是否包含login,如果包含,说明是登录操作,放行。
        if (url.contains("login")){
            log.info("登录操作");
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }

        //3.获取请求头中的令牌( token) 。
        String jwt =req.getHeader("token");

        //4.判断令牌是否存在,如果不存在,返回错误结果(未登录)。
        if (!StringUtils.hasLength(jwt)){ //工具类判断是否有长度
            log.info("请求头token为空,返回未登录信息");
            Result error =  Result.error("NOT_LOGIN");
            //手动将数据转为json格式再返回  -- 阿里工具包fastJson
            String notLogin = JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);
            return;

        }
        //5.解析token,如果解析失败,返回错误结果(未登录)。
        try {
            JwtUtils.parseJWT(jwt);
        }catch (Exception e){  //jwt令牌解析失败
            e.printStackTrace();
            log.info("解析令牌失败,返回未登录错误信息");
            Result error =  Result.error("NOT_LOGIN");
            //手动将数据转为json格式再返回  -- 阿里工具包fastJson
            String notLogin = JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);
            return;
        }
        //6.放行。
        log.info("令牌合法,放行");
        filterChain.doFilter(servletRequest, servletResponse);
    }
}

<!--        fastJson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>


4. 拦截器Interceptor

4.1  入门概述

① 概念:是一种动态拦截方法调用的机制,类似于过滤器。Spring框架中提供的,用来动态拦截控制器方法的执行。

② 作用:拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码。

③ 快速入门:

boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler)目标资源方法执行前执行,放回true:放行,返回false:不放行
void postHandle (HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)目标资源方法执行后执行
void afterCompletion (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)视图渲染完毕后执行,最后执行
  1. 定义拦截器,实现Handlerlnterceptor接口,并重写其所有方法。
  2. 注册拦截器。
@Configuration //当前类是配置类
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //注册拦截器
        registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");
    }
}

@Component
public class LoginCheckInterceptor implements HandlerInterceptor {

    @Override  //目标资源方法运行前执行,返回true:放行,返回false:不放行
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle ...");
        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 ...");
    }
}

4.2  拦截器详解(拦截路径-执行流程)

① 拦截路径:拦截器可以根据需求,配置不同的拦截路径:

public void addInterceptors(InterceptorRegistry registry) {
// addPathPatterns:需要拦截哪些资源
// excludePathPatterns:不需要拦截哪些资源
    registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**").excludePathPatterns("/login");
}
拦截路径含义举例
/*一级路径能匹配/depts,/emps,/login,不能匹配/depts/1
/**任意级路径能匹配/depts , /depts/1 , /depts/1/2
/depts /*/depts下的一级路径能匹配/depts/1,不能匹配/depts/1/2,/depts
/depts/**/depts下的任意级路径能匹配/depts, /depts/1, /depts/1/2,不能匹配/emps/1

② 执行流程:

当我们打开浏览器访问部署在Web服务器下的应用时,我们所定义的过滤器会拦截到这次请求。而由于我们目前是基于SpringBoot开发的,所以过滤器放行后是进入到了spring环境中,就会访问我们所定义的Controller中的接口方法。

在之前我们在请求响应文章学习时,我们了解到Tomcat服务器是无法识别我们所编写的Controller程序,但是是识别Servlet程序的,因为Tomcat是一个Servlet容器。而在SpringWeb环境当中就给我们提供了一个核心的Servlet - 前端控制器(DispatcherServlet)。

请求会先进入DispatcherServlet,由其再将请求转给Controller执行对应接口方法。但是目前我们又定义了拦截器,所以在执行Controller接口方法之前,先要被拦截器拦截,接下来再对请求进行处理。

当过滤器Filter 和 拦截器Interceptor同时存在时,执行流程为:

 ③ Filter 与 Interceptor对比:

  • 接口规范不同:过滤器需要实现Filter接口,而拦截器需要实现HandlerInterceptor接口。
  • 拦截范围不同:过滤器Filter会拦截所有的资源,而Interceptor只会拦截Spring环境中的资源。

4.3  案例:拦截器Interceptor实现登录校验

与过滤器Filter逻辑与实现步骤完全一致,只是将基础方案由过滤器转换为拦截器即可。

//@Slf4j
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {

    @Override  //目标资源方法运行前执行,返回true:放行,返回false:不放行
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //进行登录校验登录
        //1.获取请求url。
        String url = request.getRequestURL().toString();
        //log.info("请求的url:{}",url)

        //2.判断请求url中是否包含login,如果包含,说明是登录操作,放行。
        if (url.contains("login")){
            //log.info("登录操作");
            return true;
        }

        //3.获取请求头中的令牌( token) 。
        String jwt =request.getHeader("token");

        //4.判断令牌是否存在,如果不存在,返回错误结果(未登录)。
        if (!StringUtils.hasLength(jwt)){ //工具类判断是否有长度
            //log.info("请求头token为空,返回未登录信息");
            Result error =  Result.error("NOT_LOGIN");
            //手动将数据转为json格式再返回  -- 阿里工具包fastJson
            String notLogin = JSONObject.toJSONString(error);
            response.getWriter().write(notLogin);
            return false;

        }
        //5.解析token,如果解析失败,返回错误结果(未登录)。
        try {
            JwtUtils.parseJWT(jwt);
        }catch (Exception e){  //jwt令牌解析失败
            e.printStackTrace();
            //log.info("解析令牌失败,返回未登录错误信息");
            Result error =  Result.error("NOT_LOGIN");
            //手动将数据转为json格式再返回  -- 阿里工具包fastJson
            String notLogin = JSONObject.toJSONString(error);
            response.getWriter().write(notLogin);
            return false;
        }
        //6.放行。
        //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 ...");
    }
}

@Configuration //当前类是配置类
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //注册拦截器
        registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");
    }
}

三、异常处理

首先我们通过前端页面执行业务操作,我们先来观察系统出现异常之后会发生的现象,再思考如何处理异常。

当我们在部门管理中新增部门操作添加就业部发现页面并没有发生变化,并且F12控制台输出情况:Duplicate entry '就业部' for key 'dept.name' 

并且当我们查找返回值时发现,与我们预设返回的时间、状态码、错误描述信息和请求路径也不同:


异常处理:程序开发过程中不可避免的会遇到异常现象。(出现异常时,默认返回的结果不符合规范)所以我们思考出现异常我们该如何处理异常呢?

  • 方案一:在Controller的方法中进行 try...catch处理。(代码臃肿,不推荐
  • 方案二:定义全局异常处理器。(简单、优雅、推荐

/**
 * 全局异常处理器
 * 定义在exception包下
 */
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)  //所有的异常
    public Result ex(Exception ex){
        //输出异常堆栈信息
        ex.printStackTrace();
        //响应错误结果
        return Result.error("操作失败,请联系管理员");
    }

注意:@RestControllerAdvice = @ControllerAdvice + @ResponseBody

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/460571.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

iview render函数(vue render函数)

iview 的render函数就是vue的render函数&#xff0c;iview常用在表格里面自定义内容&#xff0c;下面来看render函数常用的配置&#xff1a; 1、 h是createdElement的简写&#xff0c;有3个参数&#xff1a; 语法&#xff1a;render:(h,params)>{} render:(h,params) >…

mulesoft MCIA 破釜沉舟备考 2023.04.25.24(易错题)

mulesoft MCIA 破釜沉舟备考 2023.04.25.24(易错题) 1. An insurance company is using a CIoudHub runtime plane.2. A mule application must periodically process a large dataset which varies from 6 GB lo 8 GB from a back-end database and write transform data lo…

LSTM简单介绍—然后使用LSTM对FashionMNIST数据集处理

文章目录 LSTM 简单介绍LSTM的基本结构LSTM的工作原理输入门遗忘门输出门细胞状态更新输出计算 总结代码实例 LSTM 简单介绍 在自然语言处理、语音识别等领域&#xff0c;长短时记忆网络 (Long Short-Term Memory, LSTM) 已经成为了常用的模型之一。本文将介绍 LSTM 的基本结构…

gpt在线使用-免费的 GPT在哪下载

免费的 GPT&#xff08;Generative Pre-trained Transformer&#xff09; 。现在您可以免费体验我们的 GPT 技术&#xff0c;来让您的业务或项目更加智能。 GPT 是一种基于最前沿的自然语言处理技术&#xff0c;它展现出了令人惊叹的预测能力和交互性能。我们的 GPT 是在世界顶…

警惕读书无用论,要知道一个人最可怕的就是精神世界的贫瘠和荒凉

孔乙已是鲁迅笔下人物&#xff0c;穷困流倒还穿着象征读书人的长衫&#xff0c;迁腐、麻木。最近&#xff0c;大家自我调佩是“当代孔乙己”&#xff0c;学历成为思想负担&#xff0c;找工作时高不成低不就。 一、社会对于学历和职业之间的关系认知是怎样的&#xff1f; 学历不…

Forefront GPT-4免费版:开启无限畅聊时代,乐享人工智能快感,无限制“白嫖”,还能和N多角色一起聊天?赶紧注册,再过些时间估计就要收费了

目录 前言注册登录方式应用体验聊天体验绘图体验 “是打算先免费后收费吗&#xff1f;”建议其它资料下载 前言 近期&#xff0c;人工智能技术迎来重大飞跃&#xff0c;OpenAI的ChatGPT等工具成为全球数亿人探索提高生产力和增强创造力的新方法。人们现在可以使用人工智能驱动…

绩效管理系统有哪些推荐?

绩效管理系统有哪些推荐&#xff1f;市面上的绩效管理系统五花八门&#xff0c;这就来给大家推荐几款优质的&#xff01; 一、如何选择绩效管理系统 在选择绩效管理系统之前&#xff0c;需要先考虑以下几个问题&#xff1a; 了解你的企业目标和需求&#xff1a;在选择绩效管…

nacos注册中心替换成eureka

背景 项目使用的springcloud、nacos、redis等插件&#xff0c;但是nacos比较重&#xff0c;小项目使用不到&#xff0c;想用一个tomcat部署项目&#xff0c;所以准备用eureka替换nacos&#xff1b; eureka Eureak 是Netflix 开源微服务框架中一系列项目中的一个。Spring Clo…

JVM 垃圾收集器

一&#xff0c;常用的垃圾收集器 如果说收集算法是内存回收的方法论&#xff0c;那么垃圾收集器就是内存回收的具体实现。 如下图为年轻代和老年代的垃圾回收器&#xff0c;划线表示可以同时存在。 1&#xff0c;Serial Serial收集器是最基本、发展历史最悠久的收集器&…

怎么把录音文件转换成mp3格式,3个高效方法

在工作中&#xff0c;我们可能会选择录音来记录会议内容&#xff0c;以便之后整理会议纪要。但是我们知道录音文件的格式千差万别。比如在手机上录制的音频文件通常以M4A、WAV等多种格式存储&#xff0c;然而这些格式可能会存在不兼容的问题&#xff0c;导致我们无法在其他平台…

openEuler Developer Day 2023成功召开!发布嵌入式商业版本及多项成果

【中国&#xff0c;上海&#xff0c;2023年4月21日】openEuler Developer Day 2023于4月20-21日在线上和线下同步举办。本次大会由开放原子开源基金会指导&#xff0c;中国软件行业协会、openEuler社区、边缘计算产业联盟共同主办&#xff0c;以“万涓汇流&#xff0c;奔涌向前…

3DEXPERIENCE MODSIM产品前期概念结构快速开发方案(下) | 达索系统百世慧®

基于3DEXPERIENCE单一数据源、实时多专业协同平台、附加全新CATIA建模方法与MODSIM建模仿真一体化技术&#xff0c;助力产品设计与仿真效率提升&#xff0c;产品多学科性能提升&#xff0c;产品轻量化减重等&#xff0c;全方位赋能产品前期概念结构高效高质开发。 目录 达索系…

利用css实现视差滚动和抖动效果

背景&#xff1a; 前端的设计效果&#xff0c;越来越炫酷&#xff0c;而这些炫酷的效果&#xff0c;利用css3的动画效果和js就可以实现&#xff0c;简单的代码就能实现非常炫酷的效果。 原理&#xff1a; 利用 js监控scrollTop的位置&#xff0c;通过 top定位图片的位置&#x…

halcon灰度积分投影/垂直积分投影

简介:关于灰度投影积分可以用到的场合很多,例如分割字符,分割尺子上的刻度等,适用于有规律的变化这些内容的检测。本文复现了论文《基于深度学习和灰度纹理特征的铁路接触网绝缘子状态检测》中灰度积分投影实现了对绝缘子缺陷位置的检测。见(图1)灰度积分垂直方向投影获得…

JAVAWeb09-WEB 工程路径专题

1. 工程路径问题 先看一个问题 index.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>base 标签</title> </head> <body> <h1>注册用户~~</h1> <!--解读:1.…

创建型模式-建造者模式

建造者模式 概述 将一个复杂对象的构建与表示分离&#xff0c;使得同样的构建过程可以创建不同的表示 这个模式适用于&#xff1a;某个对象的构建过程复杂的情况 将部件的构造与装配分离&#xff0c;由 Builder 负责构造&#xff0c;Director 进行装配&#xff0c;实现了构…

LeetCode——新手村

目录 前言 一、一维数组的动态和 1、题目 2、代码 二、将数字变成 0 的操作次数 1、题目 2、代码 三、最富有客户的资产总量 1、题目 2、代码 四、Fizz Buzz 1、题目 2、代码 五、链表的中间结点 1、题目 2、代码 六、赎金信 1、题目 2、代码 前言 注册了一个LeetCode的…

10、Mysql常见面试题

Mysql常见面试题 文章目录 Mysql常见面试题一 Mysql索引001 Mysql如何实现的索引机制&#xff1f;002 InnoDB索引与MyISAM索引实现的区别是什么&#xff1f;003 一个表中如果没有创建索引&#xff0c;那么还会创建B树吗&#xff1f; 004 说一下B树索引实现原理&#xff08;数据…

毕业5年的同学突然告诉我,他已经是年薪30W的自动化测试工程师....

作为一名程序员&#xff0c;都会对自己未来的职业发展而焦虑。一方面是因为IT作为知识密集型的行业&#xff0c;知识体系复杂且知识更新速度非常快&#xff0c;“一日不学就会落后”。 另外一方面&#xff0c;IT又是劳动密集型的行业&#xff0c;不仅业人员多&#xff0c;而且个…

8个你可能不知道的令人震惊的 HTML 技巧

大厂面试题分享 面试题库 前后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 web前端面试题库 VS java后端面试题库大全 1. 捕获属性打开你的设备摄像头 正如 input 标记具有 email、 text 和 password 属性一样&…