登录功能
登录校验(重点)
登录校验指的是在服务器接收到浏览器发送过来的请求之后,首先要对这个请求进行校验,先要校验一下用户登录了没有
怎么来实现登录校验的操作呢?具体的实现思路可以分为两部分:
-
在员工登录成功后,需要将用户登录成功的信息存起来,记录用户已经登录成功的标记。
-
在浏览器发起请求时,需要在服务端进行统一拦截,拦截后进行登录校验。
会话技术
会话跟踪技术:
Cookie(客户端会话跟踪技术)
数据存储在客户端浏览器当中
服务器端在给客户端在响应数据的时候,会自动 的将 cookie 响应给浏览器,浏览器接收到响应回来的 cookie 之后,会自动 的将 cookie 的值存储在浏览器本地。接下来在后续的每一次请求当中,都会将浏览器本地所存储的 cookie 自动地携带到服务端。
Session(服务端会话跟踪技术)
数据存储在储在服务端
底层就是基于cookie实现的
令牌技术
令牌就是一个用户身份的标识,本质就是一个字符串
如果通过令牌技术来跟踪会话,我们就可以在浏览器发起请求。在请求登录接口的时候,如果登录成功,我就可以生成一个令牌,令牌就是用户的合法身份凭证。接下来我在响应数据的时候,我就可以直接将令牌响应给前端。
接下来我们在前端程序当中接收到令牌之后,就需要将这个令牌存储起来。这个存储可以存储在 cookie 当中,也可以存储在其他的存储空间(比如:localStorage)当中。
接下来,在后续的每一次请求当中,都需要将令牌携带到服务端。携带到服务端之后,接下来我们就需要来校验令牌的有效性。如果令牌是有效的,就说明用户已经执行了登录操作,如果令牌是无效的,就说明用户之前并未执行登录操作。
此时,如果是在同一次会话的多次请求之间,我们想共享数据,我们就可以将共享的数据存储在令牌当中就可以了。
JWT令牌
简洁:是指jwt就是一个简单的字符串。可以在请求参数或者是请求头当中直接传递。
自包含:指的是jwt令牌,看似是一个随机的字符串,但是我们是可以根据自身的需求在jwt令牌中存储自定义的数据内容。如:可以直接在jwt令牌中存储用户的相关信息。
简单来讲,jwt就是将原始的json数据格式进行了安全的封装,这样就可以直接基于jwt在通信双方安全的进行信息传输了。
java
// 生成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,"wujiaowujiao")//签名算法
.setClaims(claims)//自定义内容(载荷)
.setExpiration(new Date(System.currentTimeMillis()+3600*1000))//设置有效期为1h
.compact();
System.out.println(jwt);
}
// 解析JWT
@Test
public void testParseJWT(){
Claims claims = Jwts.parser()
.setSigningKey("wujiaowujiao")//指定签名密钥
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidG9tIiwiaWQiOjEsImV4cCI6MTczMTc0MDk2MH0.XNxy4S4R9U_PyYf17xNeHLHIaFIFmB3fSQW7xwCm2SM")//解析令牌
.getBody();
System.out.println(claims);
}
过滤器Filter
快速入门
filter是Javaweb三大组件之一,并不是springboot当中提供的功能,在springboot项目中,要想使用javaweb的三大组件,必须在启动类上面加上一个注解@ServletComponentScan
详解
执行流程
过滤器拦截到了请求之后,首先先执行放行之前的逻辑,放行之前的逻辑执行完毕之后,执行放行操作,放行就是调用 FilterChain对象当中的doFilter()方法,在调用doFilter()这个方法之前所编写的代码属于放行之前的逻辑。
在放行后访问完 web 资源之后还会回到过滤器当中,回到过滤器之后如有需求还可以执行放行之后的逻辑,放行之后的逻辑我们写在doFilter()这行代码之后。
拦截路径
过滤器链
过滤器链指的是在一个web应用程序当中,可以配置多个过滤器,多个过滤器就形成了一个过滤器链。
通过控制台日志的输出,大家发现AbcFilter先执行DemoFilter后执行,这是为什么呢?
其实是和过滤器的类名有关系。以注解方式配置的Filter过滤器,它的执行优先级是按时过滤器类名的自动排序确定的,类名排名越靠前,优先级越高。
登录校验-Filter
因为tomcat传入的request实际上是HTTPrequest 这里是多态的思想
看一下源码就可以了,这里传递的是父类接口,这样子接口就可以完美兼容父接口
不懂不懂为什么
在tomcat那一章节,tomcat会将解析后的请求信息封装到一个对象中,这个对象就是httpservletrequest,我有这个印象
测试这块没跟,之后可以再看一下。
拦截器Interceptor
简介&快速入门
在拦截器当中,我们通常也是做一些通用性的操作,比如:我们可以通过拦截器来拦截前端发起的请求,将登录校验的逻辑全部编写在拦截器当中。在校验的过程当中,如发现用户登录了(携带JWT令牌且是合法令牌),就可以直接放行,去访问spring当中的资源。如果校验时发现并没有登录或是非法令牌,就可以直接给前端响应未登录的错误信息。
下面我们通过快速入门程序,来学习下拦截器的基本使用。拦截器的使用步骤和过滤器类似,也分为两步:
-
定义拦截器
-
注册配置拦截器
详解
拦截路径
执行流程
tomcat服务器不识别所编写的control程序,但是它识别servlet程序,因为tomcat是一个servlet容器,在springweb环境中,提供了一个非常核心的servlet,叫做前端控制器(DispatcherServlet)
-
当我们打开浏览器来访问部署在web服务器当中的web应用时,此时我们所定义的过滤器会拦截到这次请求。拦截到这次请求之后,它会先执行放行前的逻辑,然后再执行放行操作。而由于我们当前是基于springboot开发的,所以放行之后是进入到了spring的环境当中,也就是要来访问我们所定义的controller当中的接口方法。
-
Tomcat并不识别所编写的Controller程序,但是它识别Servlet程序,所以在Spring的Web环境中提供了一个非常核心的Servlet:DispatcherServlet(前端控制器),所有请求都会先进行到DispatcherServlet,再将请求转给Controller。
-
当我们定义了拦截器后,会在执行Controller的方法之前,请求被拦截器拦截住。执行
preHandle()
方法,这个方法执行完成后需要返回一个布尔类型的值,如果返回true,就表示放行本次操作,才会继续访问controller中的方法;如果返回false,则不会放行(controller中的方法也不会执行)。 -
在controller当中的方法执行完毕之后,再回过来执行
postHandle()
这个方法以及afterCompletion()
方法,然后再返回给DispatcherServlet,最终再来执行过滤器当中放行后的这一部分逻辑的逻辑。执行完毕之后,最终给浏览器响应数据。
登录校验
登录校验的过滤器和拦截器,我们只需要使用其中的一种就可以了。
这次测试跟了。
java
package com.wujiao.interceptor;
import com.alibaba.fastjson.JSONObject;
import com.wujiao.pojo.Result;
import com.wujiao.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;
@Component
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override//目标资源方法运行前运行,即controller方法,返回true;放行,返回false,不放行
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {
System.out.println("preHandle...");
// 1. 获取请求url
String 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");
log.info("从请求头中获取的令牌:{}",jwt);
// 4. 判断令牌是否存在,如果不存在,返回错误结果(未登录)
if(!StringUtils.hasLength(jwt)){
log.info("请求头token为空,返回未登录的信息");
Result error = Result.error("NOT_LOGIN");
String notLogin = JSONObject.toJSONString(error);//把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)
res.getWriter().write(notLogin);
return false;
}
// 5. 解析token,如果解析失败,返回错误结果(未登录)
try {
JwtUtils.parseJWT(jwt);
} catch (Exception e) {//jwt解析失败
log.info("解析令牌失败,返回未登录错误信息");
Result error = Result.error("NOT_LOGIN");
String notLogin = JSONObject.toJSONString(error);//把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)
res.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...");
}
}
异常处理
程序开发过程中不可避免的会遇到异常现象
出现异常时,默认返回的结果不符合规范,即返回的不是json格式的数据
以上就是全局异常处理器的使用,主要涉及到两个注解:
-
@RestControllerAdvice //表示当前类为全局异常处理器
-
@ExceptionHandler //指定可以捕获哪种类型的异常进行处理
java
package com.wujiao.exception;
import com.wujiao.pojo.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
//全局异常处理器
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)//捕获所有异常
public Result ex(Exception ex){
ex.printStackTrace();
return Result.error("对不起,操作失败,请联系管理员");
}
}
及时复习呀