SpringBootWeb案例_03

Web后端开发_06

SpringBootWeb案例_03

登录认证

智能学习辅助系统登录时需要身份验证

1.登录功能

先实现简单的登录功能,在进一步优化。

1.1需求

若账户或密码不存在/密码不正确,则登录失败。

账户密码正确,则登录成功

image-20231130160508878

1.2接口文档

1.2.1登录-基本信息

请求路径:/login

请求方式:POST

接口描述:该接口用于员工登录Tlias智能学习辅助系统,登录完毕后,系统下发JWT令牌。

1.2.2请求参数

参数格式:application/json

参数说明:

名称类型是否必须备注
usernamestring必须用户名
passwordstring必须密码

请求数据样例:

{"username": "jinyong","password": "123456"
}
1.2.3响应数据

参数格式:application/json

参数说明:

名称类型是否必须默认值备注其他信息
codenumber必须响应码, 1 成功 ; 0 失败
msgstring非必须提示信息
datastring必须返回的数据 , jwt令牌

响应数据样例:

{"code": 1,"msg": "success","data": "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoi6YeR5bq4IiwiaWQiOjEsInVzZXJuYW1lIjoiamlueW9uZyIsImV4cCI6MTY2MjIwNzA0OH0.KkUc_CXJZJ8Dd063eImx4H9Ojfrr6XMJ-yVzaWCVZCo"
}
1.2.4备注说明

用户登录成功后,系统会自动下发JWT令牌,然后在后续的每次请求中,都需要在请求头header中携带到服务端,请求头的名称为 token ,值为 登录时下发的JWT令牌。

如果检测到用户未登录,则会返回如下固定错误信息:

{"code": 0,"msg": "NOT_LOGIN","data": null
}

1.3思路

image-20231130161143128

1.4功能实现

LoginController.java登录的控制层

@Slf4j
@RestController
public class LoginController {@Autowiredprivate 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("用户名或密码错误");}
}

EmpService.java员工管理的service的接口

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

EmpServiceImpl.java员工 接口的实现类

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

EmpMapper.java员工的Mapper层

@Mapper
public interface EmpMapper {/*** 根据用户名和密码查询员工** @param emp* @return*/@Select("select * from emp where username = #{username}  and password = #{password}")Emp getByUsernameAndPassword(Emp emp);
}

1.5API测试

登录失败:

image-20231130163729544

登录成功:

image-20231130163852941

1.6前端联调

登录失败示例:

image-20231130170425098

后端返回数据

image-20231130170602828

登陆成功示例:

image-20231130170312592

登陆成功

image-20231130170340485

后端返回数据

image-20231130170654140

问题

在未登录情况下,我们也可以直接访问部门管理、员工管理等功能。

image-20231130171717139

2.登录校验

思路

image-20231130172047260

登录标记

会话技术:用户登录成功之后,每一次请求中,都可以获取到该标记

统一拦截器

  • 过滤器:Filter
  • 拦截器:Interceptor

2.1会话技术

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

image-20231130173454366

2.2会话跟踪方案对比
2.2.1Cookie

image-20231130175721874

  • 优点:HTTP协议中支持的技术
  • 缺点:
    • 移动端APP无法使用Cookie
    • 不安全,用户可以自己禁用Cookie
    • Cookie不能跨域

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

image-20231130180154543

示例

cookie

@Slf4j
@RestController
public class SessionController {//设置Cookie@GetMapping("/c1")public Result cookie1(HttpServletResponse response) {response.addCookie(new Cookie("login_username", "Bowen"));//设置Cookie/响应Cookiereturn Result.success();}//获取Cookie@GetMapping("/c2")public Result cookie2(HttpServletRequest request) {Cookie[] cookies = request.getCookies();//获取所有的Cookiefor (Cookie cookie : cookies) {if (cookie.getName().equals("login_username")) {//输出name为login_username的cookieSystem.out.println("login_username:" + cookie.getValue());}}return Result.success();}
}

cookie1

image-20231130180838307

image-20231130181004780

cookie2

image-20231130181122057

2.2.2Session

image-20231130181633744

  • 优点:存储在服务端,安全
  • 缺点:
    • 服务集群环境下无法直接使用Session
    • Cookie的所有缺点

服务集群环境

示例

session

@Slf4j
@RestController
public class SessionController {@GetMapping("/s1")public Result session1(HttpSession session){log.info("HttpSession-s1: {}", session.hashCode());session.setAttribute("loginUser", "tom"); //往session中存储数据return Result.success();}@GetMapping("/s2")public Result session2(HttpServletRequest request){HttpSession session = request.getSession();log.info("HttpSession-s2: {}", session.hashCode());Object loginUser = session.getAttribute("loginUser"); //从session中获取数据log.info("loginUser: {}", loginUser);return Result.success(loginUser);}
}

session1

访问http://localhost:8080/s1,可以看到响应头多了一行数据

Set-Cookie: JSESSIONID=E6162850B6461136FB3B6E47141BE345; Path=/; HttpOnly

image-20231130182518144

并且Application中存储了该cookie

JSESSIONID=E6162850B6461136FB3B6E47141BE345代表服务器端session对象的ID

image-20231130182552262

session2

访问http://localhost:8080/s2可以看到请求头中的

Cookie: Idea-28469084=333be614-a4f0-4420-bb00-7ef0ad6f47ab; sidebarStatus=0; login_username=Bowen; JSESSIONID=E6162850B6461136FB3B6E47141BE345

image-20231130182643590

控制台输出的日志中,两次请求拿到的session是同一个,都是295124489

image-20231130183713773

2.2.3令牌技术(主流方案)

image-20231130183945036

  • 优点:

    • 支持PC端、移动端

    • 解决集群环境下的认证问题

    • 减轻服务器端存储压力

  • 缺点:需要自己实现

2.3JWT令牌

2.3.1JWT(将原始的JSON数据格式进行了安全的封装)

简介

  • 全称:JSON Web Token(https://jwt.io)
  • 定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的。
  • 组成:
    • 第一部分:Header(头),记录令牌类型、签名算法等。例如:{"alg":"HS256","type":"JWT"}
    • 第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。例如:{"id":"1","username":"Tom"}
    • 第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload,并加入指定秘钥,通过指定签名算法计算而来。

image-20231130204742116

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

2.3.2场景:登录认证
  1. 登录成功后,生成令牌
  2. 后续每个请求,都要携带JWT令牌,系统在每次请求处理前,先校验令牌,通过后,再处理
2.3.3JWT-生成

pom.xml引入依赖

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

生成令牌

@SpringBootTest
class TliasWebManagementApplicationTests {/*** 生成JWT令牌*/@Testpublic void testGenJwt() {Map<String, Object> claims = new HashMap<>();claims.put("id", 1);claims.put("name", "Tom");String jwt = Jwts.builder().signWith(SignatureAlgorithm.HS256, "Bowen")//签名算法.setClaims(claims)//自定义内容(荷载).setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))//设置有效期1h.compact();System.out.println(jwt);}
}

image-20231130211942884

生成的令牌

eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiVG9tIiwiaWQiOjEsImV4cCI6MTcwMTM1Mzk2NH0.Bn4qfOumxnzuwkFrBxGw3MQa4fdYf8rOCRRUbL02f1M

可以将令牌粘贴到官网进行解析:https://jwt.io/

image-20231130212655814

2.3.4JWT-校验
@SpringBootTest
class TliasWebManagementApplicationTests {/*** 解析JWT*/@Testpublic void testPareJwt() {Claims claims = Jwts.parser().setSigningKey("Bowen").parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiVG9tIiwiaWQiOjEsImV4cCI6MTcwMTM1MzA3OX0.8dK3aG5KhIjmPd9rxGZ_QW0BXMhWacycD9-jhqW4GKg").getBody();System.out.println(claims);}
}

image-20231130213046402

注意事项

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

思路

  • 令牌生成:登录成功后,生成JWT令牌,并返回给前端
  • 令牌校验:在请求到达服务端后,对令牌进行统一拦截、校验
2.3.5.1接口文档:见1.2接口文档

步骤

  • 引入JWT令牌操作工具类
  • 登录完成后,调用工具类生成JWT令牌,并返回
2.3.5.2JwtUtils.java工具类
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;}
}
2.3.5.3LoginController.java登录的控制层
@Slf4j
@RestController
public class LoginController {@Autowiredprivate 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());String jwt = JwtUtils.generateJwt(claims);//jwt包含了当前登录的员工信息return Result.success(jwt);}//登录失败,返回错误信息return Result.error("用户名或密码错误");}
}
2.3.5.4API测试

用户密码正确时

image-20231130220314925

用户或密码错误时

image-20231130220531340

2.3.5.5前后端联调

登录成功可以抓取去tlias_token的值eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoi6YeR5bq4IiwiaWQiOjEsInVzZXJuYW1lIjoiamlueW9uZyIsImV4cCI6MTcwMTM5NjQyOH0.N9-lpBDXMI_Bj_Bhy0Y6WBx1EcsTxBPA0IDnVqUhMm4

image-20231130221218297

进入员工管理页面,可以找到与tlias_token中一样的token

image-20231130221750000

2.4过滤器Filter

过滤器Filter

概述

  • 概念:Filter过滤器是JavaWeb三大组件(Servlet、Filter、Listener)之一。
  • 过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能。
  • 过滤器一般完成一些通用的操作,比如:登录校验统一编码处理敏感字符处理等。

image-20231130223040901

2.4.1快速入门
  1. 定义Filter:定义一个类,实现Filter接口,并重写其所有方法。
  2. 配置Filter:Filter类上加@WebFilter注解,配置拦截资源的路径。引导类上加@ServletComponentScan开启Servlet组件支持。

image-20231130223702016

DemoFilter.javaFilter类

@WebFilter(urlPatterns = "/*")//urlPatterns = "/*" 拦截所有请求
public class DemoFilter implements Filter {@Override//初始化方法只调用一次public void init(FilterConfig filterConfig) throws ServletException {
//        Filter.super.init(filterConfig);System.out.println("init 初始化方法执行了");}@Override//拦截到请求之后调用,调用多次public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("拦截到了请求");//放行filterChain.doFilter(servletRequest, servletResponse);}@Override//销毁方法,只调用一次public void destroy() {
//        Filter.super.destroy();System.out.println("destroy 销毁方法执行了");}
}

TliasWebManagementApplication.java启动类

@ServletComponentScan//开启了对servlet组件的支持
@SpringBootApplication
public class TliasWebManagementApplication {public static void main(String[] args) {SpringApplication.run(TliasWebManagementApplication.class, args);}}

image-20231130225822087

image-20231130225914992

2.4.2详情(执行流程、拦截路径、过滤器链)
2.4.2.1执行流程

image-20231130230255100

  • 放行后访问对应资源,资源访问完成后,会回到Filter中
  • 如果回到Filter中,只执行放行后的逻辑
2.4.2.2拦截路径

执行流程搞清楚之后,接下来再来介绍一下过滤器的拦截路径,Filter可以根据需求,配置不同的拦截资源路径:

拦截路径urlPatterns值含义
拦截具体路径/login只有访问 /login 路径时,才会被拦截
目录拦截/emps/*访问/emps下的所有资源,都会被拦截
拦截所有/*访问所有资源,都会被拦截

示例

拦截了/depts下的一级路径,(调试的时候,在放行处打断点)

@WebFilter(urlPatterns = "/depts/*")//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("拦截到了请求...放行前的逻辑");//放行filterChain.doFilter(servletRequest, servletResponse);System.out.println("拦截到了请求...放行后的逻辑");}@Override//销毁方法,只调用一次public void destroy() {System.out.println("destroy 销毁方法执行了");}
}
2.4.2.3过滤器链

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

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

image-20231130233937073

2.4.2.4示例
@WebFilter("/*")
public class AbcFilter implements Filter {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("Abc拦截到了请求...放行前的逻辑");//放行filterChain.doFilter(servletRequest, servletResponse);System.out.println("Abc拦截到了请求...放行后的逻辑");}
}

API测试

image-20231130233344550

控制台输出日志

image-20231130233436111

2.4.3小结
  1. 执行流程
    • 请求==》放行前逻辑==》放行==》资源==》放行后逻辑
  2. 拦截路径
    • /login
    • /depts/*
    • /*
  3. 过滤器链
    • 一个web应用中,配置了多个过滤器,就形成了一个过滤器链
2.4.4登录校验-Filter
2.4.4.1需求分析

需要使用过滤器Filter来完成案例当中的登录校验功能。

image-20230107095010089

先来回顾下前面分析过的登录校验的基本流程:

  • 要进入到后台管理系统,必须先完成登录操作,此时就需要访问登录接口login。

  • 登录成功之后,会在服务端生成一个JWT令牌,并且把JWT令牌返回给前端,前端会将JWT令牌存储下来。

  • 在后续的每一次请求当中,都会将JWT令牌携带到服务端,请求到达服务端之后,要想去访问对应的业务功能,此时必须先要校验令牌的有效性。

  • 对于校验令牌的这一块操作,使用登录校验的过滤器,在过滤器当中来校验令牌的有效性。如果令牌是无效的,就响应一个错误的信息,也不会再去放行访问对应的资源了。如果令牌存在,并且它是有效的,此时就会放行去访问对应的web资源,执行相应的业务操作。

大概清楚了在Filter过滤器的实现步骤了,那在正式开发登录校验过滤器之前,思考两个问题:

  1. 所有的请求,拦截到了之后,都需要校验令牌吗?

    • 答案:登录请求例外
  2. 拦截到请求后,什么情况下才可以放行,执行业务操作?

    • 答案:有令牌,且令牌校验通过(合法);否则都返回未登录错误结果
2.4.4.2具体流程

Filter过滤器的流程步骤:

image-20230112122130564

基于上面的业务流程分析出具体的操作步骤:

  1. 获取请求url
  2. 判断请求url中是否包含login,如果包含,说明是登录操作,放行
  3. 获取请求头中的令牌(token)
  4. 判断令牌是否存在,如果不存在,返回错误结果(未登录)
  5. 解析token,如果解析失败,返回错误结果(未登录)
  6. 放行
2.4.4.3功能实现
引入阿里巴巴fastJSON依赖
<!--fastJSON-->
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83</version>
</dependency>
LoginCheckFilter.java登录过滤器
/*** @ClassName LoginCheckFilter* @Description 登录过滤器* @Author Bowen* @Date 2023/11/30 23:53* @Version 1.0**/
@Slf4j
@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletRequest req = (HttpServletRequest) request;HttpServletResponse resp = (HttpServletResponse) response;//> 1. 获取请求urlString url = req.getRequestURL().toString();log.info("请求的url:{}", url);//> 2. 判断请求url中是否包含login,如果包含,说明是登录操作,放行if (url.contains("login")) {log.info("登录操作,放行。。。");chain.doFilter(request, response);return;}//> 3. 获取请求头中的令牌(token)String jwt = req.getHeader("token");//> 4. 判断令牌是否存在,如果不存在,返回错误结果(未登录)if (!StringUtils.hasLength(jwt)) {log.info("请求token为空,返回未登录的信息");Result error = Result.error("NOT_LOGIN");//手动转换 对象--JSON =============》阿里巴巴fastJSONString notLogin = JSONObject.toJSONString(error);resp.getWriter().write(notLogin);return;}//> 5. 解析token,如果解析失败,返回错误结果(未登录)try {JwtUtils.parseJWT(jwt);//选中代码块后ctrl+alt+t} catch (Exception e) {//解析失败e.printStackTrace();log.info("解析令牌失败,返回未登录错误信息");Result error = Result.error("NOT_LOGIN");//手动转换 对象--JSON =============》阿里巴巴fastJSONString notLogin = JSONObject.toJSONString(error);resp.getWriter().write(notLogin);return;}//> 6. 放行log.info("令牌合法,放行");chain.doFilter(request, response);}}

注释掉DemoFilter.javaAbcFilter中的过滤器

2.4.4.4API测试

启动springboot,进行测试

将登录拿到的jwt令牌复制给查询部门的测试接口

令牌eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoi6YeR5bq4IiwiaWQiOjEsInVzZXJuYW1lIjoiamlueW9uZyIsImV4cCI6MTcwMTQwNTY1M30.rT2wkW2OoY_UZz75A36ElRVvmQh5ivv214RzXZp3yHk

image-20231201004134781

添加请求头的token后,发送请求,获取到了数据

image-20231201004401524

若请求参数没有token,则返回JSON数据

image-20231201004608657

查看控制台日志

image-20231201005147906

进行前后端联调

登录后拿到 员工的urlhttp://localhost:90/#/system/emp,退出登录,复制该url到地址栏,直接转跳到登录页面。前后端联调成功~~~

image-20231201005543762

将拿到的url复制到地址栏

image-20231201005840088

直接跳转到登录页面,说明拦截器配置成功

image-20231201005744868

2.5拦截器Interceptor

2.5.1简介

概述

  • 概念:是一种动态拦截方法调用的机制,类似于过滤器。Spring框架中提供的,用于动态拦截控制器方法的执行。
  • 作用:拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码。

image-20231201100545157

2.5.2快速入门
  1. 定义拦截器,实现HandlerInterceptor接口,并重写其所有方法
  2. 注册拦截器

image-20231201101127141

image-20231201101243805

2.5.2.1示例
定义拦截器

com.bowen包下创建interceptor.LoginCheckInterceptor.java

@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....");}
}
配置拦截器

com.bowen包下创建config.WebConfig.java配置类

@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate LoginCheckInterceptor loginCheckInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");}
}

注释掉之前的拦截器,启动该项目

API测试

使用登录接口测试

preHandle方法中返回值为true时放行。

image-20231201105312139

控制台日志

image-20231201105457316

preHandle方法中返回值为false时禁止放行。

image-20231201105640094

控制台日志

image-20231201105803855

2.5.3详解(拦截路径、执行流程)
2.5.3.1拦截路径
  • 拦截器可以根据需求,配置不同的拦截路径

image-20231201110845868

拦截路径含义举例
/*一级路径能匹配/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

示例1

addPathPatterns("/**").excludePathPatterns("/login")

excludePathPatterns("/login")排除登录请求

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

API测试-登录接口

登录返回数据令牌成功,并拿到令牌:eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoi6YeR5bq4IiwiaWQiOjEsInVzZXJuYW1lIjoiamlueW9uZyIsImV4cCI6MTcwMTQ1MzM0OX0.EPBM2H_BPFi71Sl4cUs3rzYGuA9xCdsFTPUOxfgNgkY

image-20231201135848087

查看控制台日志,发现直接登录了没有被拦截

image-20231201135958388

API测试-查询部门接口

需要登录后拿到的令牌作为token

image-20231201140152152

查看控制台日志,发现查询部门时拦截后放行,拿到数据,再执行postHandle、afterCompletion方法

image-20231201140416031

API测试-删除部门接口

image-20231201140748389

查看控制台日志,发现删除部门时拦截后放行,更新数据,再执行postHandle、afterCompletion方法

image-20231201141027022

示例2

addPathPatterns("/*").excludePathPatterns("/login")

addPathPatterns("/*")拦截一级路径

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

API测试-查询部门

查询部门是一级路径,应该被拦截

image-20231201141633981

查看日志,查询部门请求被拦截

image-20231201141826713

API测试-删除部门

删除部门有二级路径,应该没有拦截

image-20231201142013428

查看控制台生成的日志

image-20231201142153004

2.5.3.2执行流程

image-20231201142842440

FilterInterceptor

接口规范不同:过滤器需要实现Fileter接口,而拦截器需要实现HandlerInterceptor接口。

拦截范围不同:过滤器Fileter会拦截所有的资源,而Interceptor只会拦截Spring环境中的资源。

2.5.3.3示例

打开DemoFilter.java中的过滤器,打开WebConfig.java中的拦截器,重新启动服务进行测试

API测试-查询部门

image-20231201143932423

查看控制台日志

image-20231201144344811

2.5.4登录校验-Interceptor
2.5.4.1流程图

流程图与Filter过滤器的流程完全一致

image-20230112122130564

基于上面的业务流程分析出具体的操作步骤:

  1. 获取请求url
  2. 判断请求url中是否包含login,如果包含,说明是登录操作,放行
  3. 获取请求头中的令牌(token)
  4. 判断令牌是否存在,如果不存在,返回错误结果(未登录)
  5. 解析token,如果解析失败,返回错误结果(未登录)
  6. 放行
2.5.4.2功能实现

WebConfig.java配置类,在config包下,其中的.excludePathPatterns("/login")可省略,因为在LoginCheckInterceptor.java//> 2. 判断请求url中是否包含login,如果包含,说明是登录操作,放行,所以造成了重复判断,可删除该拦截器。

/*** @ClassName WebConfig* @Description 配置类* @Author Bowen* @Date 2023/12/1 10:20* @Version 1.0**/
@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate LoginCheckInterceptor loginCheckInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**").excludePathPatterns("/login");}
}

LoginCheckInterceptor.java实现登录校验的拦截器

/*** @ClassName LoginCheckInterceptor* @Description 拦截器`Interceptor`演示* @Author Bowen* @Date 2023/12/1 10:13* @Version 1.0**/
@Slf4j
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {@Override//目标资源方法运行前,返回true:放行,返回false,不放行public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {System.out.println("preHandle....");//> 1. 获取请求urlString url = req.getRequestURL().toString();log.info("请求的url:{}", url);//> 2. 判断请求url中是否包含login,如果包含,说明是登录操作,放行if (url.contains("login")) {log.info("登录操作,放行。。。");return true;}//> 3. 获取请求头中的令牌(token)String jwt = req.getHeader("token");//> 4. 判断令牌是否存在,如果不存在,返回错误结果(未登录)if (!StringUtils.hasLength(jwt)) {log.info("请求token为空,返回未登录的信息");Result error = Result.error("NOT_LOGIN");//手动转换 对象--JSON =============》阿里巴巴fastJSONString notLogin = JSONObject.toJSONString(error);resp.getWriter().write(notLogin);return false;}//> 5. 解析token,如果解析失败,返回错误结果(未登录)try {JwtUtils.parseJWT(jwt);//选中代码块后ctrl+alt+t} catch (Exception e) {//解析失败e.printStackTrace();log.info("解析令牌失败,返回未登录错误信息");Result error = Result.error("NOT_LOGIN");//手动转换 对象--JSON =============》阿里巴巴fastJSONString notLogin = JSONObject.toJSONString(error);resp.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....");}
}

注释掉filter包下的所有过滤器注解

启动服务进行测试

2.5.4.3API测试
API测试-登录

image-20231201150047334

API测试-查询部门(未登录)

image-20231201150207302

API测试-查询部门(已登录)

image-20231201150401104

查看日志

image-20231201150628649

前后端联调

启动nginx,不再具体演示,具体操作可参考2.4.4.4API测试

image-20231201150747547

3.异常处理

3.1问题引出

打开浏览器,访问系统中的新增部门操作,系统中已经有了 “就业部” 这个部门,再来增加一个就业部,看看会发生什么现象。

image-20231201155913598

点击确定之后,窗口关闭了,页面没有任何反应,就业部也没有添加上。 而此时会发现,网络请求报错了。

image-20231201160045153

响应回来的数据是一个JSON格式的数据。但这种JSON格式的数据还是我们开发规范当中所提到的统一响应结果Result吗?显然并不是。由于返回的数据不符合开发规范,所以前端并不能解析出响应的JSON数据。

状态码为500,表示服务器端异常,打开idea,查看服务器端出了什么问题。在项目案例中没有进行异常处理

image-20231201160331122

上述错误信息的含义是,dept部门表的name字段的值 就业部 重复了,因为在数据库表dept中已经有了就业部,之前设计这张表时,为name字段建议了唯一约束,所以该字段的值是不能重复的。

而当再添加就业部,这个部门时,就违反了唯一约束,此时就会报错。

3.2全局异常处理器

image-20231201160748800

@RestControllerAdvice = @ControllerAdvice + @ResponseBody

新建一个包exception,创建GlobalExceptionHandler.java全局异常处理器

/*** @ClassName GlobalExceptionHandler* @Description 全局异常处理器* @Author Bowen* @Date 2023/12/1 16:10* @Version 1.0**/
@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)//捕获所有异常public Result ex(Exception ex) {ex.printStackTrace();return Result.error("对不起操作失败请联系管理员~~~");}
}

添加断点后进行调试,

image-20231201161720173

重新添加重复的部门

image-20231201161757624

点击确定后,进入控制层的断点,恢复程序进入下一个断点

image-20231201161859680

可以看到抛出异常

image-20231201162015424

前端返回异常弹窗

image-20231201162147325

3.3总结

全局异常处理器

  • @RestControllerAdvice
  • @ExceptionHandler

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

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

相关文章

git基本概念

一、版本控制概念 1.1 什么是版本控制 1.1.1 手动管理文件版本 1.1.2 版本控制软件 概念&#xff1a;版本控制软件是一个用来记录文件发生的变化&#xff0c;以便将来查阅特定版本修订情况的系统&#xff0c;有时也叫“版本控制系统”。通俗的理解就是把手工管理文件版本的方…

关于电脑提示vcruntime140_1.dll无法继续执行代码的解决办法

vcruntime140_1.dll是Visual C运行时库的一个组成部分&#xff0c;它包含了大量用于支持C应用程序运行时的功能。这个文件通常在开发和使用C程序时被调用&#xff0c;特别是在使用Microsoft Visual Studio进行开发时。vcruntime140_1.dll文件丢失或损坏会导致C程序无法正常运行…

信息化,数字化,智能化是3种不同概念吗?与机械化,自动化矛盾吗?

先说结论&#xff1a; 1、信息化、数字化、智能化确实是3种不同的概念&#xff01; 2、这3种概念与机械化、自动化并不矛盾&#xff0c;它们是制造业中不同发展阶段和不同层次的概念。 机械化&#xff1a;是指在生产过程中使用机械技术来辅助人工完成一些重复性、单一性、劳…

助力android面试2024【面试题合集】

转眼间&#xff0c;2023年快过完了。今年作为口罩开放的第一年大家的日子都过的十分艰难&#xff0c;那么想必找工作也不好找&#xff0c;在我们android开发这一行业非常的卷&#xff0c;在各行各业中尤为突出。android虽然不好过&#xff0c;但不能不吃饭吧。卷归卷但是还得干…

Pytorch——多卡GPU训练与单卡GPU训练相互切换

部分深度学习网络默认是多卡并行训练的&#xff0c;由于某些原因&#xff0c;有时需要指定在某单卡上训练&#xff0c;最近遇到一个&#xff0c;这里总结如下。 目录 一、多卡训练1.1 修改配置文件1.2 修改主训练文件1.3 显卡使用情况 二、单卡训练2.1 修改配置文件2.2 显卡使…

简单了解下IP的全球划分【笔记】

国际互联网号码分配机构 (The Internet Assigned Numbers Authority&#xff0c;简称IANA&#xff09;。它是互联网名称与数字地址分配机构&#xff08;The Internet Corporation for Assigned Names and Numbers&#xff0c;简称ICANN&#xff09;旗下的一个机构&#xff0c;主…

Linux5-计划任务、进程

计划任务 一、cron 计划任务 周期性计划任务 cron 任务概述 • 用途:按照设置的时间间隔为用户反复执行某一项固定的系统任务 • 软件包&#xff1a;cronie、crontabs • 系统服务&#xff1a;crond • 日志文件&#xff1a;/var/log/crond 管理计划任务策略 • 使用 cro…

存储虚拟化的写入过程

存储虚拟化的场景下&#xff0c;整个写入的过程。 在虚拟机里面&#xff0c;应用层调用 write 系统调用写入文件。write 系统调用进入虚拟机里面的内核&#xff0c;经过 VFS&#xff0c;通用块设备层&#xff0c;I/O 调度层&#xff0c;到达块设备驱动。虚拟机里面的块设备驱动…

uniapp uni-popup组件在微信小程序中滚动穿透问题

起因 在微信小程序中使用uni-popup组件时&#xff0c;出现滚动穿透&#xff0c;并且uni-popup内部内容不会滚动问题。 解决 滚动穿透 查阅官方文档&#xff0c;发现滚动穿透是由于平台差异性造成的&#xff0c;具体解决可以参照文档禁止滚动穿透 <template><page-…

【概率统计】如何理解概率密度函数及核密度估计

文章目录 概念回顾浅析概率密度函数概率值为0&#xff1f;PDF值大于1&#xff1f;一个栗子 核密度估计如何理解核密度估计核密度估计的应用 总结 概念回顾 直方图&#xff08;Histogram&#xff09;&#xff1a;直方图是最直观的一种方法&#xff0c;它通过把数据划分为若干个区…

软件工程 - 第8章 面向对象建模 - 2 静态建模

静态建模&#xff08;类和对象建模&#xff09; 类和对象模型的基本模型元素有类、对象以及它们之间的关系。系统中的类和对象模型描述了系统的静态结构&#xff0c;在UML中用类图和对象图来表示。 类图由系统中使用的类以及它们之间的关系组成。类之间的关系有关联、依赖、泛…

Numpy进阶

NumPy进阶80题完整版

卓扬网林荣雄说:开盘30分钟便能看清股票的涨跌

在一些人眼中&#xff0c;炒股是一件再简单不过的事情了&#xff0c;他们认为只需要简单地买进卖出&#xff0c;就可以玩转股票了。话虽如此&#xff0c;但是要想真正立足于股市&#xff0c;简单的买进卖出是远远不够的。要想长期屹立于股市之中&#xff0c;广大股民还需要积累…

Adobe ColdFusion文件读取漏洞(CVE-2010-2861)

任务一&#xff1a; 复现漏洞 任务二&#xff1a; 尝试利用漏洞读取目标系统中的“opt/coldfusion8/license.txt"文件 1.环境搭建&#xff08;网上写的密码是admin&#xff0c;就用admin&#xff09; 2.看答案就是一层一层进行路径穿越攻击&#xff0c;这里要注意如果…

【动态规划】LeetCode-63.不同路径II

&#x1f388;算法那些事专栏说明&#xff1a;这是一个记录刷题日常的专栏&#xff0c;每个文章标题前都会写明这道题使用的算法。专栏每日计划至少更新1道题目&#xff0c;在这立下Flag&#x1f6a9; &#x1f3e0;个人主页&#xff1a;Jammingpro &#x1f4d5;专栏链接&…

matlab diff和gradient

gradient 求解梯度。 示例 FX gradient(F) 返回向量 F 的一维数值梯度。输出 FX 对应于 ∂F/∂x&#xff0c;即 x&#xff08;水平&#xff09;方向上的差分。点之间的间距假定为 1。 使用方法&#xff1a; x -2:0.2:2; y x’; z x .* exp(-x.^2 - y.^2); [px,py] gradien…

数据库-MySQL之数据库必知必会17-21章

第17章 组 合 查 询 创建组合查询 可用UNION操作符来组合数条SQL查询。利用UNION&#xff0c;可给出多条SELECT语句&#xff0c;将它们的结果组合成单个结果集。 **例子&#xff1a;**假如需要价格小于等于5的所有物品的一个列表&#xff0c;而且还想包括供应商1001和1002生产…

【Linux】:信号(三)捕捉

信号捕捉 一.sigaction1.基本使用2.sa_mask字段 二.可重入函数三.volatile四.SIGCHLD信号 承接上文 果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下: 用户程序注册了SIGQUIT信…

MOS管的静电击穿问题

MOS管输入电阻很高&#xff0c;为什么一遇到静电就不行了&#xff1f; 静电击穿&#xff1a;由于静电的积累导致电压超过了原本MOS的绝缘能力&#xff0c;导致电流突然增大的现象。 MOS管基础知识了解&#xff1a; G极(gate)—栅极&#xff0c;不用说比较好认 S极(source)—源…

基于SSM框架的餐馆点餐系统的设计

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…