目录
会话技术
用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应
会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据
会话跟踪方案
客户端会话跟踪技术:Cookie
@Slf4j
@RestController
public class CookieController {
//设置cookie
@GetMapping("c1")
public Result cookie(HttpServletResponse response){
response.addCookie(new Cookie("login_username","itheima"));
return Result.success();
}
//获取cookie
@GetMapping("c2")
public Result cookie2(HttpServletRequest request){
Cookie[] cookies=request.getCookies();//获取所有的cookie
for(Cookie cookie:cookies){
if (cookie.getName().equals("login_username")){
System.out.println("login_username:"+cookie.getValue());
}
}
return Result.success();
}
}
服务端会话跟踪技术:Session
//往httpSession中存储
@GetMapping("s1")
public Result session1(HttpSession session){
log.info("HttpSession-s1:{}",session.hashCode());
session.setAttribute("loginUser","tom");//往session中存储
return Result.success();
}
//往httpSession中存储
@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);
}
令牌技术(JWT令牌)
JWT定义
定义了一种简洁的自包含模式,用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的
应用场景:登录认证
登陆成功后生成令牌,后续每个请求,都要携带JWT令牌,系统在每次请求处理之前,先校验令牌,通过后,再处理
生成JWT
//生成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))//设置有效期为1小时
.compact();
System.out.println(jwt);
}
解析JWT
//解析JWT
@Test
public void testParseJwt(){
Claims claims=Jwts.parser()
.setSigningKey("itheima")//指定签名密钥
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidG9tIiwiaWQiOjEsImV4cCI6MTcyMTc5MzA3Nn0.sr9O2q23mb85qVNRABlL9hj0KCMfkYtoRYEn7t3T5qM")//解析令牌
.getBody();
System.out.println(claims);
}
生成令牌以后,篡改任意一个位置都会报错!
JWT校验时使用的签名密钥,必须和生成的JWT令牌时使用的密钥时配套的
如果JWT令牌解析校验时报错,则说明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());
String jwt= JwtUtils.generateJwt(claims);//jwt包含了当前登录的员工信息
return Result.success(jwt);
}
//登陆失败,返回信息
return Result.error("用户名或者密码错误");
}
}
public class JwtUtils {
private static String signKey="itheima";
private static Long expire=43200000L;
/*
生成JWT令牌
JWT第二部分负载payload中存储的内容
*/
public static String generateJwt(Map<String,Object> claims){
String jwt= Jwts.builder()
.addClaims(claims)
.signWith(SignatureAlgorithm.HS256,signKey)//签名算法
.setExpiration(new Date(System.currentTimeMillis()+expire))//设置有效期为1小时
.compact();
return jwt;
}
/*
解析
*/
public static Claims paraseJwt(String jwt){
Claims claims=Jwts.parser()
.setSigningKey(signKey)//指定签名密钥
.parseClaimsJws(jwt)//解析令牌
.getBody();
return claims;
}
}
Filter
入门
@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("拦截到了请求");
//放行
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {//销毁方法,只调用一次
System.out.println("destroy销毁方法执行了");
}
}
这里如果没有放行操作的话,访问了这个网址但是不会显示数据,只有放行以后,才可以有数据显示。
@ServletComponentScan //加上这个才能对servlet组件的加载
@SpringBootApplication
public class SpringPractice1Application {
public SpringPractice1Application() {
}
public static void main(String[] args) {
SpringApplication.run(SpringPractice1Application.class, args);
}
}
详解
执行流程
请求-->放行前逻辑--> 放行--> 资源--> 放行后逻辑
拦截路径
过滤器链
比如AbcFilter和DemoFilter 就是AbcFilter先执行
登录校验Filter的实现
思路
@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;
//获取请求url
String url=req.getRequestURL().toString();
log.info("获取url:{}",url);
//判断url中是否包含login,如果包含,登录请求,放行
if(url.contains("login")){
log.info("登录操作,放行");
filterChain.doFilter(servletRequest,servletResponse);
return;
}
//获取请求头中的令牌(token)
String jwt=req.getHeader("token");
//判断令牌是否存在,如果不存在,返回错误结果(未登录)
if(!StringUtils.hasLength(jwt)){
log.info("请求头为空,返回未登陆信息");
Result error=Result.error("NOT_LOGIN");
//手动转换对象为json-----阿里巴巴
String notLogin=JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);
return;
}
//解析token,如果解析失败,返回错误信息(未登录)
try {
JwtUtils.paraseJwt(jwt);
}catch (Exception e){//jwt解析失败
log.info("解析令牌失败,返回未登录错误信息");
Result error=Result.error("NOT_LOGIN");
//手动转换对象为json
String notLogin=JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);
return;
}
//放行
log.info("令牌合法,放行");
filterChain.doFilter(servletRequest,servletResponse);
}
把对象转化为json,需要引入阿里巴巴的依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
登录操作时的后台
拦截器Interceptor
一种动态拦截方法调用的机制,类似于过滤器,Spring框架中提供的,用来动态拦截控制器方法的执行
作用:拦截请求,在指定方法调用前后,根据业务需要执行预先设定的代码
入门
定义拦截器
//定义拦截器
@Component//交给ioc容器
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("psothandle...");
}
@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("/**");//拦截所有
}
}
详解
拦截路径
执行流程
与Filter的区别
登录校验Interceptor的实现
@Configuration //配置类
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginCheckInterceptor loginCheckInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");//拦截所有
}
}
//定义拦截器
@Component//交给ioc容器
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override//目标资源方法运行前运行,返回true:放行,返回false:不放行
public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
//获取请求url
String url=req.getRequestURL().toString();
log.info("获取url:{}",url);
//判断url中是否包含login,如果包含,登录请求,放行
if(url.contains("login")){
log.info("登录操作,放行");
return true;
}
//获取请求头中的令牌(token)
String jwt=req.getHeader("token");
//判断令牌是否存在,如果不存在,返回错误结果(未登录)
if(!StringUtils.hasLength(jwt)){
log.info("请求头为空,返回未登陆信息");
Result error=Result.error("NOT_LOGIN");
//手动转换对象为json-----阿里巴巴
String notLogin= JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);
return false;
}
//解析token,如果解析失败,返回错误信息(未登录)
try {
JwtUtils.paraseJwt(jwt);
}catch (Exception e){//jwt解析失败
log.info("解析令牌失败,返回未登录错误信息");
Result error=Result.error("NOT_LOGIN");
//手动转换对象为json
String notLogin=JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);
return false;
}
//放行
log.info("令牌合法,放行");
return true;
}
@Override//目标资源运行后运行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("psothandle...");
}
@Override//视图渲染完毕后运行,最后运行
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion"
);
}
}
异常处理
/*
全局异常处理器
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)//捕获所有异常
public Result ex(Exception ex){
ex.printStackTrace();
return Result.error("对不起,操作失败了,请联系管理员");
}
}
@RestControllerAdvice=@ControllerAdvice+@ResponseBody