登录功能:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginInfo {
private Integer id; //用户ID
private String username; //用户名
private String name; //姓名
private String token; // 令牌
}
empService接口:
/**
* 登录
* @param emp
* @return
*/
LoginInfo login(Emp emp);
}
empServiceImpl实现类:
a
empMapper接口:
/**
* 根据用户名和密码查询
* @param emp
* @return
*/
@Select("select * from emp where username = #{username} and password = #{password}")
Emp getByUserNameAndPwd(Emp emp);
}
empServiceImpl实现类:
@Override
public LoginInfo login(Emp emp) {
Emp e = empMapper.getByUserNameAndPwd(emp);
if (e != null){ //登录成功 --生成令牌
Map<String,Object> map = new HashMap<>(); //设置 payload的数据
map.put("id",e.getId());
map.put("username",e.getUsername());
map.put("name",e.getName());
String jwt = JwtUtils.generateJwt(map);
return new LoginInfo(e.getId(),e.getUsername(),e.getName(),jwt);
}
return null;
}
LoginController:
@Autowired
private EmpService empService;
@PostMapping("/login")
public Result login(@RequestBody Emp emp){
log.info("用户进行登录了...");
LoginInfo loginInfo = empService.login(emp);
if (loginInfo != null){
return Result.success(loginInfo);
}
return Result.error("用户名或密码错误~~");
}
引子:
登录校验:
会话技术:
简单的理解,一次会话,就像我们的一次打电话。
断开了一个。
会话跟踪:
会话跟踪方案:
客户端会话跟踪技术:Cookie
cookie的三个自动:
优点:
缺点:
编写SessionController控制器层:
//设置Cookie
@GetMapping("/c1")
public Result cookie1(HttpServletResponse response){
response.addCookie(new Cookie("login_username","sde666")); //设置Cookie/响应Cookie
return Result.success();
}
启动项目,在浏览器控制栏,输入:http://localhost:8080/s1 访问
在SessionController类中,编写接口 c2 获取cookie
//获取Cookie
@GetMapping("/c2")
public Result cookie2(HttpServletRequest request){
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if(cookie.getName().equals("login_username")){
System.out.println("login_username: "+cookie.getValue()); //输出name为login_username的cookie
}
}
return Result.success();
}
执行 http://localhost:8080/c2 获取cookie
看看浏览器控制台:
区分跨域的三个维度:
服务端会话跟踪技术:Session
优点:
缺点:
编写 SessionController控制器:
/**
* 存session
* @param session
* @return
*/
@GetMapping("/s1")
public Result session1(HttpSession session){
log.info("HttpSession-s1: {}", session.hashCode());
session.setAttribute("loginUser", "tom"); //往session中存储数据
return Result.success();
}
在浏览器输入http://localhost:8080/s1访问
编写:http://localhost:8080/s2 取session
/**
* 取session
* @param request
* @return
*/
@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);
}
通过浏览器访问:
通过idea控制台观看:
JWT令牌:
优点:
缺点:
全称:
组成:
jwt依赖:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
生成令牌:
编写JwtTest类:
/**
* 生成令牌
*/
@Test
void testGenJwt(){
Map<String,Object> claims = new HashMap<>();
claims.put("id",18);
claims.put("username","孙尚香");
String jwt = Jwts.builder()
.signWith(SignatureAlgorithm.HS256,"c2Rl") //设置令牌的签名算法和密钥
.setExpiration(new Date(System.currentTimeMillis() + 2 * 3600 * 1000)) //设置过期时间
.addClaims(claims) //往指定令牌存入数据
.compact(); //生成令牌
System.out.println(jwt);
}
运行测试类:
解析令牌:
把上面刚刚,生成的令牌进行校验
/**
* 解析令牌
*/
@Test
void testParseJwt(){
Claims claims = Jwts.parser()
.setSigningKey("c2Rl") //
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MDI3MDc1NjksImlkIjoxOCwidXNlcm5hbWUiOiLlrZnlsJrpppkifQ.XW__woxh-1YVdR2rILavgr3tMPCqKjPjTioFC02MaZ4")
.getBody(); //获取自定义内容
System.out.println(claims);
}
看idea控制台:
jwt令牌解析失败的4个情况:
演示一:(改写jwt令牌header部分)
测试效果:
演示二(更改令牌Payload部分)
效果测试:
演示三(更改signature部分)
效果演示:
演示四(jwt令牌过期)
运行这个单元测试方法,生成jwt令牌,放到下面的程序中解析。
进行解析测试:
完成登录成功下发Jwt令牌的操作:
测试效果:
测试一下登录失败的情况。
过滤器Filter:

概念:
快速操作:
我们可以看到, 当我们实现Filter接口时,程序会报错。
上面有三个方法,要实现,也可以只实现一个方法,那就是doFilter方法。因为init方法和destory方法都是默认实现了已经。
@Slf4j
public class DemoFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("DemoFilter init 方法执行了... ");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
log.info("DemoFilter diFilter 方法执行了 放行前执行...");
chain.doFilter(servletRequest,servletResponse); //放行
log.info("DemoFilter diFilter 方法执行了 放行后执行...");
}
@Override
public void destroy() {
log.info("DemoFilter destory 方法执行了...");
}
}
配置Filter:
引导类上加注解:
@ServletComponentScan //开启spring boot对javaweb组件的支持(filter)
@SpringBootApplication
public class TilasRun {
public static void main(String[] args) {
SpringApplication.run(TilasRun.class, args);
}
}
运行测试:
在Apifox里面发送请求进行测试:
关系应用程序:
Filter详解:
执行流程:
过滤器的放行:
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
log.info("DemoFilter diFilter 方法执行了 放行前执行...");
chain.doFilter(servletRequest,servletResponse); //放行
log.info("DemoFilter diFilter 方法执行了 放行后执行...");
}
如果我们没有调用 FilterChain接口中的 doFilter方法,那么就代表没有放行。
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
log.info("DemoFilter diFilter 方法执行了 放行前执行...");
// chain.doFilter(servletRequest,servletResponse); // 我把这一行注释了,表示不放行,那么请求就被拦截在这了。
log.info("DemoFilter diFilter 方法执行了 放行后执行...");
}
重启服务器测试:
调用查询全部的部门接口
调用登录接口:
看看 idea的控制台信息:
拦截路径解析:
拦截具体路径:
@WebFilter("/login") //拦截 login请求
public class DemoFilter implements Filter {
}
测试:
查询全部的部门信息:
登录接口:
目录拦截:
@Slf4j
//@WebFilter(urlPatterns = "/*")
//@WebFilter("/login") //拦截 login请求
@WebFilter(urlPatterns = "/depts/*") //拦截depts目录下的所有请求
public class DemoFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("DemoFilter init 方法执行了... ");
}
}
测试:
登录接口:
查看所有部门接口:
拦截所有:
过滤器链:
介绍:
顺序:
创建AbcFilter类:
@Slf4j
@WebFilter("/*")
public class AbcFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
log.info("AbcFilter doFilter 方法执行了 放行前");
chain.doFilter(servletRequest,servletResponse); // 放行
log.info("AbcFilter doFilter 方法执行了 放行后");
}
}
还有一个filter类就是上面用的DemoFilter类
@Slf4j
@WebFilter(urlPatterns = "/*")
//@WebFilter("/login") //拦截 login请求
//@WebFilter(urlPatterns = "/depts/*") //拦截depts目录下的所有请求
public class DemoFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("DemoFilter init 方法执行了... ");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
log.info("DemoFilter diFilter 方法执行了 放行前执行...");
chain.doFilter(servletRequest,servletResponse); //放行
log.info("DemoFilter diFilter 方法执行了 放行后执行...");
}
@Override
public void destroy() {
log.info("DemoFilter destory 方法执行了...");
}
}
这两个过滤器,就成了过滤器链了,我们启动程序观察执行流程。
开始:
在Apifox里面发送请求测试:
第一步:
第二步:
第三步:
第四步:
第五步:
最后:
看看idea的控制台:
登录校验-Filter:
登录拦截步骤:
1,获取请求的url,2,判断请求头中是否包含login
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//获取请求url。
String url = request.getRequestURL().toString();
//判断请求url中是否包含login,如果包含,说明是登录操作,放行。
if (url.contains("/login")){
log.info("是登录请求,直接放行{}",url);
chain.doFilter(request,response);
return;
}
}
3,获取请求头中的令牌:
//获取请求头中的令牌(token)。
String jwt = request.getHeader("token");
4,判断令牌是否存在:
//判断令牌是否存在,如果不存在,响应401。
if (!StringUtils.hasLength(jwt)){
log.info("没有携带jwt令牌",jwt);
//如果没有 jwt令牌,设置响应码为 401
response.setStatus(401);
return;
}
5,解析令牌:
//解析token,如果解析失败,响应401 。
try {
JwtUtils.parseJWT(jwt);
} catch (Exception e) {
log.info("令牌解析出错{}",e);
response.setStatus(401);
return;
}
6,放行:
//放行。
chain.doFilter(request,response);
登录测试:
我在Apifox里面进行登录测试,看看拦截器是否直接放行了,登录接口。
查看拦截器:
直接放行,访问web资源(LoginController)
测试,jwt令牌为null的情况。
拦截到了请求:
令牌为null
看Apifox返回的信息:
测试不合法的jwt令牌:
拦截,因为不是登录请求。
有jwt令牌:
非法的jwt令牌,解析出现异常。
在Apifox查看效果:
成功测试-jwt令牌合法
不是登录请求,拦截。
有令牌:
合法的令牌:
成功访问到web资源
Apifox的返回数据:
拦截器Interceptor:

概念:
作用:
1,定义一个类,DemoInterceptor实现 HandlerInterceptor接口。
@Slf4j
@Component
public class DemoInterceptor implements HandlerInterceptor {
// controller 方法运行之前执行 返回值是 boolean类型 true表示放行 false表示不放行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("DemoInterceptor preHandle 方法执行了");
return true;
}
// controller 方法运行之后执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("DemoInterceptor postHandle 方法执行了");
}
// 最后运行的
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("DemoInterceptor afterCompletion 方法执行了");
}
}
2,注册拦截器:
/**
* 配置类---配置拦截器
*/
@Slf4j
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private DemoInterceptor demoInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(demoInterceptor).addPathPatterns("/**"); //表示拦截所有请求的路径
}
}
在Apifox测试,是否能够拦截到我的所有请求。
方法执行前拦截到了。
web资源,运行后执行。
访问到了deptController:
最后执行的方法也执行了。
查看Apifox的返回数据。
查看一下,执行流程。
拦截器:HandlerInterceptor接口解析:
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
后两个方法:
// handlerInterceptor接口的第二个方法。
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
// handlerInterceptor接口的第三个方法。
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
过滤器路径详解:
一级路径:
任意路径:
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(demoInterceptor).addPathPatterns("/**"); //表示拦截所有请求的路径
}
测试效果:
登录接口拦截:
idea效果:
查询所有部门信息接口:
idea效果:
目录匹配一级:
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 拦截除了登录以外的所有路径
registry.addInterceptor(demoInterceptor).addPathPatterns("/depts/*");
}
测试查询单个emp的接口:
idea效果:
测试根据id查询部门信息接口:
idea效果:
目录匹配任意级:
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 拦截除了登录以外的所有路径
registry.addInterceptor(demoInterceptor).addPathPatterns("/depts/**");
}
过滤器,拦截路径的排除(excludePathPatterns):
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(demoInterceptor).addPathPatterns("/**").excludePathPatterns("/login"); //我在preHandle方法中已经判断了 .excludePathPatterns("/login");
}
测试:
ieal效果:
然后在到LoginContriller接口:
Filter和interceptot的区别: