网站视频转码软件产品网站推广
https://jwt.io/
为了防止在未登录情况下,用户也能查看信息,我们引入会话跟踪
会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应。
会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。
一、客户端会话跟踪技术:Cookie
优点:HTTP协议中支持的技术
缺点: 移动端APP无法使用Cookie;不安全,用户可以自己禁用Cookie;Cookie不能跨域
二、服务端会话跟踪技术:Session
优点:存储在服务端,安全
缺点:服务器集群环境下无法直接使用Session;Cookie的缺点
三、令牌技术
优点:支持PC端、移动端;解决集群环境下的认证问题;减轻服务器端存储压力
缺点:需要自己实现
3.1 JWT
定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的。
组成:
第一部分:Header(头), 记录令牌类型、签名算法等。 例如:{"alg":"HS256","type":"JWT"}
第二部分:Payload(有效载荷)携带一些自定义信息、默认信息等。 例如:{"id":"1","username":"Tom"}
第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload,并加入指定秘钥,通过指定签名算法计算而来。
3.2 JWT生成
引入依赖
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency>
记得引入:
<dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>2.3.1</version></dependency>
登录成功返回jwt
@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);return Result.success(jwt);}return Result.error("用户名或密码错误");}
浏览器会存储jwt,往后每次请求都会加上jwt;
3.3 过滤器Filter
3.3.1概念
Filter 过滤器,是 JavaWeb 三大组件(Servlet、Filter、Listener)之一。
过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能。
过滤器一般完成一些通用的操作,比如:登录校验、统一编码处理、敏感字符处理等。
3.3.2 配置Filter
1.定义Filter:定义一个类,实现 Filter 接口,并重写其所有方法。
2.配置Filter:Filter类上加 @WebFilter 注解,配置拦截资源的路径。引导类上加 @ServletComponentScan 开启Servlet组件支
@WebFilter(urlPatterns = "/*")//拦截所有请求
public class DemoFilter implements Filter {@Override//初始化方法,只调用一次public void init(FilterConfig filterConfig) throws ServletException {Filter.super.init(filterConfig);}@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();}
}
@ServletComponentScan//开启了对servlet组件支持
@SpringBootApplication
public class SpringbootMybatisApplication {public static void main(String[] args) {SpringApplication.run(SpringbootMybatisApplication.class, args);}}
3.3.3 过滤器链
一个web应用中,可以配置多个过滤器,这多个过滤器就形成了一个过滤器链。
3.3.4 登录校验
代码实现如下:
使用fastJson将字符串转换为Json格式,注意配置
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.76</version></dependency>
@Override//拦截到请求之后调用,调用多次public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("拦截到请求");HttpServletRequest req = (HttpServletRequest)servletRequest;HttpServletResponse resp = (HttpServletResponse) servletResponse;//1.获取请求urlString url = req.getRequestURL().toString();log.info("请求的url:{}",url);//2.判断请求url是否含login,如果包含,放行if(url.contains("login")){log.info("登录操作,放行。。。");filterChain.doFilter(servletRequest,servletResponse);return;}//3.获取请求头中的令牌tokenString jwt = req.getHeader("token");//4.判断令牌是否存在,不存在的时候if(!StringUtils.hasLength(jwt)){log.info("请求头token为空,返回未登录信息");Result error = Result.error("NOT_LOGIN");//将对象转换成jsonString notLogin = JSONObject.toJSONString(error);resp.getWriter().write(notLogin);return;}//5.解析token,如果解析失败,返回错误信息(未登录)try{JwtUtils.parseJWT(jwt);}catch (Exception e){e.printStackTrace();log.info("解析令牌失败,返回未登录错误信息");Result error = Result.error("NOT_LOGIN");//将对象转换成jsonString notLogin = JSONObject.toJSONString(error);resp.getWriter().write(notLogin);return;}//6.令牌合法放行log.info("令牌合法放行");filterChain.doFilter(servletRequest,servletResponse);}
3.4 拦截器(Interceptor)
3.4.1 概念
概念:是一种动态拦截方法调用的机制,类似于过滤器。Spring框架中提供的,用来动态拦截控制器方法的执行。
作用:拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码。
3.4.2 注册拦截器
1.定义拦截器,实现HandlerInterceptor接口,并重写其所有方法。
2.注册拦截器
拦截器可以根据需求,配置不同的拦截路径
3.4.3 登录校验
@Overridepublic boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {//1.获取请求urlString url = req.getRequestURL().toString();log.info("请求的url:{}",url);//2.判断请求url是否含login,如果包含,放行if(url.contains("login")){log.info("登录操作,放行。。。");return true;}//3.获取请求头中的令牌tokenString jwt = req.getHeader("token");//4.判断令牌是否存在,不存在的时候if(!StringUtils.hasLength(jwt)){log.info("请求头token为空,返回未登录信息");Result error = Result.error("NOT_LOGIN");//将对象转换成jsonString notLogin = JSONObject.toJSONString(error);resp.getWriter().write(notLogin);return false;}//5.解析token,如果解析失败,返回错误信息(未登录)try{JwtUtils.parseJWT(jwt);}catch (Exception e){e.printStackTrace();log.info("解析令牌失败,返回未登录错误信息");Result error = Result.error("NOT_LOGIN");//将对象转换成jsonString notLogin = JSONObject.toJSONString(error);resp.getWriter().write(notLogin);return false;}//6.令牌合法放行log.info("令牌合法放行");return HandlerInterceptor.super.preHandle(req, resp, handler);}