目录
1、基础登录
2、登录校验
2.1、会话跟踪技术:
Cookie、Session
JWT令牌
2.2、请求过滤
方式一、过滤器(Filter)
方法二:拦截器(interceptor)
Filter与Interceptor的区别
3、全局异常处理器
1、基础登录
2、登录校验
2.1、会话跟踪技术:
Cookie、Session
Cookie和Session 优缺点:
Cookie:优点:HTTP协议中支持的技术
缺点: -- 移动端APP无法使用Cookie
-- 不安全,用户可以自己禁用Cookie(保存在客户端)
-- Cookie不能跨域
Session: 优点:存储在服务端,安全
缺点:-- 服务器集群环境下无法直接使用Session
-- Cookie的所有缺点(Session的底层是Cookie)
Cookie、Session的设置与获取 :
package pearl.controller;
import pearl.pojo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* Cookie、HttpSession演示
*/
@Slf4j
@RestController
public class CookieSessionController {
//设置Cookie
@GetMapping("/c1")
public Result cookie1(HttpServletResponse response){
response.addCookie(new Cookie("login_username","itheima")); //设置Cookie/响应Cookie
return Result.success();
}
//获取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();
}
@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);
}
}
访问结果:
JWT令牌
JWT官网--详情请参考左边官方资料
JWT令牌技术
优点:-- 支持PC端、移动端
-- 解决集群环境下的认证问题
-- 减轻服务器端存储压力
缺点:需要自己实现
1、生成JWT令牌
导入依赖
<!-- JWT令牌依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
在SpringBoot测试类中新增测试方法,用于生成JWT令牌
/*
* 生成JWT令牌
* */
@Test
public void testGenJwt(){
Map<String,Object> claims = new HashMap<>(); //定义map集合用于封装自定义的数据
// 集合存数据:语法:mapname.put(key,value)
claims.put("id",1);
claims.put("name","tom");
String jwt = Jwts.builder()//构建JWT令牌 eg:指定生成数字签名的算法、秘钥(自定义)、要存取的自定义的数据
.signWith(SignatureAlgorithm.HS256, "firstkey")//两个参数:算法,秘钥
.setClaims(claims)//设置自定义的数据,可以将自定义的数据封装到map集合中
.setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))//设置令牌截止时间
// new Date()表示当前时间,里面传入参数表示参数代表的时间,System.currentTimeMillis()表示当前的毫秒数,而加上3600*1000表示一个小时后的时间
.compact(); //生成字符串类型的令牌
System.out.println(jwt);
}
生成运行结果:
eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidG9tIiwiaWQiOjEsImV4cCI6MTY4MjQyOTYzMn0.-iLHjX1bJQzRuSHMrPDU5Xk5Wca3e_12ytZ_weV6GMQ
生成的JWT令牌可在官网中解析出来
2、解析JWT令牌
在SpringBoot测试类中新增测试方法,用于解析JWT令牌
/*
* 解析JWT令牌
* */
@Test
public void testParseJwt(){
Claims claims = Jwts.parser()
.setSigningKey("firstkey")//指定签名秘钥,这里的秘钥和生成JWT令牌是设置的一致
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidG9tIiwiaWQiOjEsImV4cCI6MTY4MjQyOTYzMn0.-iLHjX1bJQzRuSHMrPDU5Xk5Wca3e_12ytZ_weV6GMQ")//传递JWT令牌
.getBody();//获取JWT令牌的自定义内容
System.out.println(claims);
}
运行结果:
{name=tom, id=1, exp=1682429632}
解析完成!
3、使用JWT令牌登录校验
这里我们只完成第一步、生成JWT令牌并返回给前端
- 新建一个工具类JWTutils 存入上方写的生成与解析JWT令牌方法
package pearl.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.Map;
public class JwtUtils {
private static String signKey = "firstkey";
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;
}
}
- 改写基础登录Controller
/*
* 登录
* */
@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("用户名或密码错误");
}
}
在这个案例中,我们选择使用JWT令牌进行会话跟踪
2.2、请求过滤
方式一、过滤器(Filter)
-- 概念: Filter过滤器,是JavaWeb三大组件(Servlet、Filter、Listener)之一。
-- 过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能。
-- 过滤器一般完成一些通用的操作,比如:登录校验、统一编码处理、敏感字符处理等。
Filter 快速入门
步骤:
1.定义Filter:定义一个类,实现Filter接口,并重写其所有方法。
2.配置Filter: Filter类上加@WebFilter注解,配置拦截资源的路径。启动类上加
@ServletComponentScan开启Servlet组件支持。
filter类:
package pearl.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(urlPatterns = "/*") //拦截的请求路由,可用正则表达式匹配 这里表示拦截所有请求
public class DemoFilter implements Filter {
@Override //初始化方法,只执行一次
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init 初始化方法执行了");
}
@Override//拦截到请求后调用,会执行多次
public void doFilter(ServletRequest Req, ServletResponse Res, FilterChain fChain) throws IOException, ServletException {
System.out.println("拦截到了请求");
System.out.println("放行前的逻辑");
// 放行
fChain.doFilter(Req,Res);
System.out.println("放行后的逻辑");
}
@Override //销毁方法,只执行一次
public void destroy() {
System.out.println("destroy 销毁方法执行了");
}
}
启动类:
过滤器链:
filter 实现登录校验
- 导入依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>
- 创建一个类,LoginFilter
package pearl.filter;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import pearl.pojo.Result;
import pearl.utils.JwtUtils;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
@WebFilter(urlPatterns = "/*")
public class LoginFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest Request, ServletResponse Response, FilterChain fChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)Request;
HttpServletResponse response = (HttpServletResponse)Response;
// 1. 获取请求url
String url = request.getRequestURI();
log.info("请求的url: {}",url);
// 2. 判断请求中是否包含login,如果包含,说明是登录操作,放行
if (url.contains("login")){
log.info("登录操作,放行");
fChain.doFilter(request,response);
return;
}
// 3. 获取请求中的令牌(token)
String jwt = request.getHeader("token");
// 4. 判断领牌是否存在,如果不存在,返回错误结果(未登录)
if (jwt == null || jwt ==""){
log.info("请求头token为空,返回错误结果");
Result error = Result.error("NOT_LOGIN");
//将对象强转为Json
String notLogin = JSONObject.toJSONString(error);
response.getWriter().write(notLogin);// 相应数据
return;
}
// 5. 解析token,如果解析失败,返回错误结果(未登录)
try {
JwtUtils.parseJWT(jwt); //jwt令牌解析报错说明,解析失败,返回错误结果
} catch (Exception e) {
e.printStackTrace();
Result error = Result.error("NOT_LOGIN");
//将对象强转为Json
String notLogin = JSONObject.toJSONString(error);
response.getWriter().write(notLogin);// 相应数据
return;
}
// 6. 放行
log.info("令牌合法,放行");
fChain.doFilter(request,response);
}
@Override
public void destroy() {
}
}
完成!
方法二:拦截器(interceptor)
概念: 是一种动态拦截方法调用的机制,类似于过滤器。Spring框架中提供的,用来动态拦 截控制器方法的执行。
作用: 拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码。
interceptor 快速入门
-- 定义拦截器,实现Handlerlnterceptor接口,并重写其所有方法。
package pearl.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override//controller方法运行之前运行,返回true放行,否则,不放行
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler)throws Exception{
System.out.println("preHandle...");
return true;
}
@Override // controller方法执行之后执行
public void postHandle(HttpServletRequest req, HttpServletResponse res, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle...");
}
@Override // 视图渲染完毕后执行,最后执行
public void afterCompletion(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion...");
}
}
-- 注册拦截器
package pearl.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import pearl.interceptor.LoginInterceptor;
@Configuration//配置类
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor; //定义一个拦截器对象
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor).addPathPatterns("/**");//这里需注意,拦截所有请求的路径是`/**`
}
}
拦截路径
执行流程
interceptor 实现登录校验
-- 定义拦截器,实现Handlerlnterceptor接口,并重写其所有方法。
package pearl.interceptor;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import pearl.pojo.Result;
import pearl.utils.JwtUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Override//controller方法运行之前运行,返回true放行,否则,不放行
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler)throws Exception{
// 1. 获取请求url
String url = req.getRequestURI();
log.info("请求的url: {}",url);
// 2. 判断请求中是否包含login,如果包含,说明是登录操作,放行
if (url.contains("login")){
log.info("登录操作,放行");
return true;
}
// 3. 获取请求中的令牌(token)
String jwt = req.getHeader("token");
// 4. 判断领牌是否存在,如果不存在,返回错误结果(未登录)
if (jwt == null || jwt ==""){
log.info("请求头token为空,返回错误结果");
Result error = Result.error("NOT_LOGIN");
//将对象强转为Json
String notLogin = JSONObject.toJSONString(error);
res.getWriter().write(notLogin);// 相应数据
return false;
}
// 5. 解析token,如果解析失败,返回错误结果(未登录)
try {
JwtUtils.parseJWT(jwt); //jwt令牌解析报错说明,解析失败,返回错误结果
} catch (Exception e) {
e.printStackTrace();
Result error = Result.error("NOT_LOGIN");
//将对象强转为Json
String notLogin = JSONObject.toJSONString(error);
res.getWriter().write(notLogin);// 相应数据
return false;
}
// 6. 放行
log.info("令牌合法,放行");
return true;
}
@Override // controller方法执行之后执行
public void postHandle(HttpServletRequest req, HttpServletResponse res, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle...");
}
@Override // 视图渲染完毕后执行,最后执行
public void afterCompletion(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion...");
}
}
-- 注册拦截器
package pearl.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import pearl.interceptor.LoginInterceptor;
@Configuration//配置类
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor; //定义一个拦截器对象
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/login");//这里需注意,拦截所有请求的路径是`/**`
}
}
Filter与Interceptor的区别
接口规范不同: 过滤器需要实现Filter接口,而拦截器需要实现HandlerInterceptor接口。
拦截范围不同: 过滤器Filter会拦截所有的资源,而Interceptor只会拦截Spring环境中的资源。文章来源:https://www.toymoban.com/news/detail-429888.html
3、全局异常处理器
新建包--exception ,在包中新建类 GlobalException文章来源地址https://www.toymoban.com/news/detail-429888.html
package pearl.exception;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import pearl.pojo.Result;
/*
* 全局异常处理器
* */
@RestControllerAdvice // 会将结果封装成json格式
public class GlobalException {
@ExceptionHandler(Exception.class) //Exception.class表示捕获所有异常
public Result ex(Exception ex){
ex.printStackTrace();
return Result.error("对不起,操作失败,请联系管理员");
}
}
到了这里,关于案例01-tlias智能学习辅助系统04-登录认证+全局异常处理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!