您现在的位置是:主页 > news > 网站直播怎样做/上海短视频培训机构
网站直播怎样做/上海短视频培训机构
admin2025/6/28 17:38:03【news】
简介网站直播怎样做,上海短视频培训机构,景德镇网站制作公司,唐山做网站的公司JWT(java web token) JWT包含三部分: Header 头部 Payload 负载 Signature 签名 其结构看起来是这样的 Header.Payload.Signature。 #JWT的组成 ##Header 在header中通常包含了两部分:token类型和采用的加密算法。{ “alg”: “HS256”, “typ”: “JWT…
JWT(java web token)
JWT包含三部分: Header 头部 Payload 负载 Signature 签名
其结构看起来是这样的 Header.Payload.Signature。
#JWT的组成
##Header
在header中通常包含了两部分:token类型和采用的加密算法。{ “alg”: “HS256”, “typ”: “JWT”}
接下来对这部分内容使用 Base64Url 编码组成了JWT结构的第一部分。
##Payload
它包含了claim, Claim是一些实体(通常指的用户)的状态和额外的元数据,有三种类型的claim:reserved, public 和 private.Reserved claims: 这些claim是JWT预先定义的,在JWT中并不会强制使用它们,而是推荐使用,常用的有 iss(签发者),exp(过期时间戳), sub(面向的用户), aud(接收方), iat(签发时间)。 Public claims:根据需要定义自己的字段,注意应该避免冲突 Private claims:这些是自定义的字段,可以用来在双方之间交换信息 负载使用的例子:{ “sub”: “1234567890”, “name”: “John Doe”, “admin”: true}
负载需要经过Base64Url编码后作为JWT结构的第二部分
##Signature
建签名需要使用编码后的header和payload以及一个秘钥,使用header中指定签名算法进行签名。例如如果希望使用HMAC SHA256算法,那么签名应该使用下列方式创建: HMACSHA256( base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret) 签名用于验证消息的发送者以及消息是没有经过篡改的。
#JWT的使用
客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。
此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息里面。另一种做法是,跨域的时候,JWT 就放在 POST 请求的数据体里面。
#JWT的使用场景
Authorization (授权) :
这是使用JWT的最常见场景。一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用。
Information Exchange (信息交换) :
对于安全的在各方之间传输信息而言,JSON Web Tokens无疑是一种很好的方式。因为JWTs可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。
#JWT和Session的区别
相同点是,它们都是存储用户信息;然而,Session是在服务器端的,而JWT是在客户端的。
Session方式存储用户信息的最大问题在于要占用大量服务器内存,增加服务器的开销。
而JWT方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力。
Session的状态是存储在服务器端,客户端只有session id;而Token的状态是存储在客户端。
JWT的客户端和服务端的原理
从下图可以看到:
- JWT是由服务端产生的,返回给客户端
- 客户端每次请求服务端的时候都要带上JWT
- 服务端去通过过滤器拦截请求,验证JWT
基于Token的身份认证是无状态的,服务器或者Session中不会存储任何用户信息。
没有会话信息意味着应用程序可以根据需要扩展和添加更多的机器,而不必担心用户登录的位置。
虽然这一实现可能会有所不同,但其主要流程如下:
1: 用户携带用户名和密码请求访问。
2: 服务器校验用户凭据。
3: 应用提供一个token给客户端。
4: 客户端存储token,并且在随后的每一次请求中都带着它。
5: 服务器校验token并返回数据。
注意:
1: 每一次请求都需要token。
2: Token应该放在请求header中。
3:我们还需要将服务器设置为接受来自所有域的请求,用Access-Control-Allow-Origin: *。
4.为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。
#JWT的使用
第一步:添加依赖
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>2.2.0</version>
</dependency>
第二步:创建工具类
import com.auth0.jwt.JWTSigner;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.internal.com.fasterxml.jackson.databind.ObjectMapper;import java.util.HashMap;
import java.util.Map;/*** JWT工具类*/
public class JWTUtils {private static final String SECRET = "XX#$%()(#*!()!KL<><MQLMNQNQJQK sdfkjsdrow32234545fdf>?N<:{LWPW";private static final String EXP = "exp";private static final String PAYLOAD = "payload";//加密,传入一个对象和有效期public static <T> String sign(T object, long maxAge) {try {final JWTSigner signer = new JWTSigner(SECRET);final Map<String, Object> claims = new HashMap<String, Object>();ObjectMapper mapper = new ObjectMapper();String jsonString = mapper.writeValueAsString(object);claims.put(PAYLOAD, jsonString);//传输相关信息claims.put(EXP, System.currentTimeMillis() + maxAge);//过期时长return signer.sign(claims);} catch (Exception e) {return null;}}//解密,传入一个加密后的token字符串和解密后的类型public static <T> T unsign(String jwt, Class<T> classT) {final JWTVerifier verifier = new JWTVerifier(SECRET);try {final Map<String, Object> claims = verifier.verify(jwt);if (claims.containsKey(EXP) && claims.containsKey(PAYLOAD)) {long exp = (Long) claims.get(EXP);long currentTimeMillis = System.currentTimeMillis();if (exp > currentTimeMillis) {String json = (String) claims.get(PAYLOAD);ObjectMapper objectMapper = new ObjectMapper();return objectMapper.readValue(json, classT);}}return null;} catch (Exception e) {return null;}}}
第三步:把token放进header中
第四步:每次请求都验证token(通过aop实现拦截)
前端需要把请求头中放入token参数,里面放入最开始后端返回的token
import com.rongji.common.annotation.Log;
import com.rongji.common.config.Constant;
import com.rongji.common.entity.LogEntity;
import com.rongji.common.service.LogService;
import com.rongji.common.utils.*;
import com.rongji.system.entity.JWTentity;
import com.rongji.system.entity.SysUserTokenEntity;
import com.rongji.system.service.SysUserService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.*;@Aspect
@Component
public class LogAspect {private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);@AutowiredLogService logService;@AutowiredSysUserService userService;@Pointcut("execution(* com.*.controller..*(..)))")public void logPointCut() {}@Around("logPointCut()")public Object around(ProceedingJoinPoint point) {//判断token信息HttpServletRequest request = HttpContextUtils.getHttpServletRequest();String token = request.getHeader("token");//如果传的值不存在:1:登录 2:过期String uri = request.getRequestURI();String method = request.getMethod();boolean isLogin = StringUtils.equals("/login", uri) && StringUtils.equals("POST", method);JWTentity user = null;R r =R.error(Constant.jwtErrorCode, "session失效,请重新登录");if (StringUtils.isEmpty(token)) {//进入的是登录界面if (!isLogin) {//其他接口:session过期return r;}} else if (!isLogin) {//校验user = JWTUtils.unsign(token, JWTentity.class);if (user == null) {//返回错误信息return r;} else {Long userId = user.getUserId();//判断是否在黑名单里面List tokens = userService.getUserToken(userId);if (tokens.contains(token)) {return r;}// 执行时长(毫秒)long time = Constant.jwtMaxAge - user.getLoginDate().getSeconds();if (0 < time && time <= Constant.jwtDelayMinAge) {return r;}//延时delayTime(token, user, userId, time);}}long beginTime = System.currentTimeMillis();// 执行方法Object result = null;try {result = point.proceed();} catch (Throwable throwable) {logger.error(throwable.toString());}// 执行时长(毫秒)long time = System.currentTimeMillis() - beginTime;//异步保存日志saveLog(point, time, user);return result;}private void delayTime(String token, JWTentity user, Long userId, long time) {if (time > 0 && time < Constant.jwtDelayMaxAge) { //延时HttpServletResponse response = HttpContextUtils.getHttpServletResponse();user.setLoginDate(new Date());String jwtToken = JWTUtils.sign(user, Constant.jwtMaxAge);response.setHeader("token", jwtToken);//保存黑名单SysUserTokenEntity userToken = new SysUserTokenEntity();userToken.setUserId(userId);userToken.setTokenId(token);userToken.setOperDate(new Date());userService.saveUserTokenEntity(userToken);}}void saveLog(ProceedingJoinPoint joinPoint, long time, JWTentity user) {MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();LogEntity sysLog = new LogEntity();//Log syslog = method.getAnnotation(Log.class);//if (syslog != null) {// 注解上的描述// sysLog.setOperation(className + "." + methodName + "()");// }// 请求的方法名String className = joinPoint.getTarget().getClass().getName();String methodName = signature.getName();sysLog.setMethod(className + "." + methodName + "()");// 请求的参数HttpServletRequest request = HttpContextUtils.getHttpServletRequest();Object[] args = joinPoint.getArgs();
// String params1 = JSONUtils.beanToJson(args[0]);// sysLog.setParams(params1);// 获取request// HttpServletRequest request = HttpContextUtils.getHttpServletRequest();StringBuffer url = request.getRequestURL();String uri = request.getRequestURI();// 设置IP地址String ip = IPUtils.getIpAddr(request);sysLog.setIp(ip);if (user != null) {Long userId = user.getUserId();if (userId != null) {sysLog.setUserId(userId);}String userName = user.getUserName();if (StringUtils.isNotEmpty(userName)) {sysLog.setUsername(userName);}}sysLog.setTime((int) time);// 系统当前时间Date date = new Date();sysLog.setGmtCreate(date);// 保存系统日志logService.save(sysLog);}
}
第五步:由于前后端分离问题,需要在response中设置参数:
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpStatus;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.stereotype.Component;import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@Component
@ServletComponentScan
@WebFilter(urlPatterns = "/*",filterName = "CORSFilter")
public class CORSFilter implements Filter {private static final long serialVersionUID = 1L;@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletResponse response = (HttpServletResponse) servletResponse;HttpServletRequest request=(HttpServletRequest)servletRequest;response.setContentType("text/plain;charset=UTF-8");response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));response.setHeader("Access-Control-Allow-Methods", "POST, GET, DELETE, PUT, OPTIONS");response.setHeader("Access-Control-Max-Age", "1728000");response.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, " +"Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");response.setHeader("Access-Control-Allow-Credentials", "true");response.setHeader("XDomainRequestAllowed","1");//添加可以返回自定义header信息response.setHeader("Access-Control-Expose-Headers","token");if ("OPTIONS".equals(request.getMethod())){//这里通过判断请求的方法,判断此次是否是预检请求,如果是,立即返回一个204状态吗,标示,允许跨域;预检后,正式请求response.setStatus(HttpStatus.SC_NO_CONTENT); //HttpStatus.SC_NO_CONTENT = 204return;}filterChain.doFilter(servletRequest,servletResponse);}@Overridepublic void destroy() {}/*** 重新封装request包装类*/class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {private String url;public MyHttpServletRequestWrapper(HttpServletRequest request,String url) {super(request);this.url=url;}@Overridepublic String getServletPath() {if(super.getDispatcherType().name().equals("REQUEST")) {return url;} else {return super.getServletPath();}}}}