做现货去哪些网站营销好呢,WordPress对接QQ聊天,c2c交易,公司网站建设调研问卷1. 应用场景
前后端分离项目保持登录状态。
问题#xff1a;ajax请求如何跨域#xff0c;将无法携带jsessionid#xff0c;这样会导致服务器端的session不可用。如何解决#xff1f;
后端#xff1a; 登录接口在验证过用户密码后#xff0c;将用户的身份信息转换成…1. 应用场景
前后端分离项目保持登录状态。
问题ajax请求如何跨域将无法携带jsessionid这样会导致服务器端的session不可用。如何解决
后端 登录接口在验证过用户密码后将用户的身份信息转换成一个特殊格式的字符串token放入响应体或响应头。 前端 浏览器收到登录成功响应后保存该token在本地当下次发送请求时取出该token并携带在请求头或请求体中。
后端 使用filter或者拦截器校验请求中的token是否有效如果有效就代表用户已登录放行。
2. 对于token的需求
(1) 可以自定义信息用户身份、权限信息 (2) 需要使得服务器端可以进行有效性校验即token只能由服务器端颁发不能由其他第三方伪造 (3) 需要能设置有效期
3. JWT的数据结构
https://jwt.io/ 官网地址 (1)HEADER //头 (2)PAYLOAD //体 (3)SIGNATURE //签名
4. 生成JWT
HMACSHA256( base64UrlEncode(header) . base64UrlEncode(payload), your-256-bit-secret ) (1)计算header的base64编码 (2)计算payload的base64编码 (3)计算签名
5. 校验token是否合法
原理重新计算token的sign与token中的sign进行比对一致则合法。 6. 校验token有效期
原理在jwt的payload中加入过期日期然后在校验token时检查是否过期
这是一个测试类需要导入Hutool工具类
package com.qf.fmall.jwt;import cn.hutool.jwt.JWT;import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashMap;public class TestJwt {public static final String secretkeyqfsyjava2302;public static void main(String[] args) throws UnsupportedEncodingException, InterruptedException {String token1 createToken();//验证token的合法性
// String tokeneyJoZWFkZXIwMSI6InRlc3QxMjMiLCJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJsdWZmeSIsImlhdCI6MTY5MzIwNjc2NiwicGhvbmUiOiIxMzg5OTk5ODg4OCIsInNleCI6MSwiYWdlIjoxOX0.UY3v3twt30GAN6iCOlTTZwA0YpwMBFDg3JyWgxp1_7U;Thread.sleep(3000);//使用hutool工具类校验Jwtboolean validate JWT.of(token1)//把token字符串传入用来构建jwt对象.setKey(secretkey.getBytes(utf-8))//传入计算签名要使用的密钥
// .verify() 只验证token值不验证时间.validate(3l);//校验jwt是否合法本质上就是重算签名并且比较签名是否一致。//这里的leeway是秒就是在原本的设定的失效时间容许容忍几秒System.out.println(validate);}public static String createToken() throws UnsupportedEncodingException {//利用hutool工具类生成JwtHashMapString, Object payloadMap new HashMap();payloadMap.put(age,19);payloadMap.put(sex,1);payloadMap.put(phone,13899998888);HashMapString, Object headMap new HashMap();headMap.put(header01,test123);String token JWT.create().addHeaders(headMap) //放入自定义Jwt 头.setSubject(luffy)//在 jwt 的payload中放入 sub代表用户身份信息.setExpiresAt(new Date(System.currentTimeMillis()1000*3))// 在jwt的payload中放入 jwt 失效时间.setIssuedAt(new Date()) //在 jwt 的payload 中 放入 jwt的签发时间.addPayloads(payloadMap) // 在jwt 的payload 中放入其他自定义内容.setKey(secretkey.getBytes(utf-8)) //放入计算jwt 需要的密钥字符串.sign();System.out.println(token);return token;
}}7. 开启shiro登录校验
1.用拦截器的方式实现
实现拦截器接口
package com.qf.fmall.interceptor;import cn.hutool.jwt.JWT;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;Component
Slf4j
public class JwtInterceptor implements HandlerInterceptor {Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//0.判断如果是浏览器发送的Option 请求直接放行String method request.getMethod();if (method.equals(OPTIONS)){return true;}//1.获取 请求头中的 tokenString token request.getHeader(token);//校验jwttokenif (tokennull){log.info(没带token拒绝访问);return false;}boolean validate JWT.of(token).setKey(qfsyjava2302.getBytes(utf-8)).validate(0);return validate;}
}注册拦截器
package com.qf.fmall.config;import com.qf.fmall.interceptor.JwtInterceptor;
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;Configuration
public class IntercepterConfig implements WebMvcConfigurer {AutowiredJwtInterceptor jwtInterceptor;Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(jwtInterceptor).addPathPatterns(/index/indeximg);}
}2.整合shiro过滤器
1.前端登录方法 GetMapping(/login)CrossOriginpublic ResultVo login(RequestParam(username) String username,RequestParam(password) String password) throws JsonProcessingException {Subject subject SecurityUtils.getSubject();UsernamePasswordToken token new UsernamePasswordToken(username,password);try {subject.login(token);Users principal (Users) subject.getPrincipal();//当用户登录成功之后创建jwt//把principal--- jsonObjectMapper mapper new ObjectMapper();//获取springboot内置的json转换对象String userjson mapper.writeValueAsString(principal);String jwt JWT.create().setSubject(userjson).setExpiresAt(new Date(System.currentTimeMillis() 1000 * 60 * 30)).setIssuedAt(new Date()).setKey(qfsyjava2302.getBytes()).sign();return ResultVo.vo(10000,jwt,principal);} catch (AuthenticationException e) {e.printStackTrace();return ResultVo.vo(112300,failed,null);}}
2.后端验证过滤器
package com.qf.fmall.filter;import cn.hutool.jwt.JWT;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.springframework.stereotype.Component;import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;Component(jwt)
Slf4j
public class JwtFilter extends AccessControlFilter {/*** 用于验证访问受保护的方法* param servletRequest* param servletResponse* param o* return* throws Exception*/Overrideprotected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {HttpServletRequest request (HttpServletRequest) servletRequest;//0.判断如果是浏览器发送的Option 请求直接放行String method request.getMethod();if (method.equals(OPTIONS)){return true;}//1.获取 请求头中的 tokenString token request.getHeader(token);//校验jwttokenif (tokennull){log.info(没带token拒绝访问);return false;}boolean validate JWT.of(token).setKey(qfsyjava2302.getBytes(utf-8)).validate(0);return validate;}/*** onAccessDenied 方法是Shiro框架中的一个回调方法当主体用户被拒绝访问特定资源时会被调用。该方法不返回布尔值。* 当主体尝试访问受保护的资源但权限不足或未认证时Shiro会触发 onAccessDenied 方法。通常它用于处理被拒绝访问的情况例如将用户重定向到错误页面或返回错误消息。* 因此说 onAccessDenied 方法返回 true 是没有意义的。该方法的目的是处理访问被拒绝的情况而不是返回布尔值。* param servletRequest* param servletResponse* return* throws Exception*/Overrideprotected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {return false;}
}3.让请求走shiro过滤器
在ShiroConfig类下写
//配置Shiro过滤器链Beanpublic ShiroFilterChainDefinition shiroFilterChainDefinition(){DefaultShiroFilterChainDefinition chainDefinition new DefaultShiroFilterChainDefinition();chainDefinition.addPathDefinition(/user/login**,anon);chainDefinition.addPathDefinition(/user/regist**,anon);chainDefinition.addPathDefinition(/error,anon);//让/index/indeximg 由自定义的shiro filter 进行处理 jwt 这个值是IOC容器中filter的名字chainDefinition.addPathDefinition(/index/indeximg,jwt);
// chainDefinition.addPathDefinition(/**,authc);return chainDefinition;}
但是shiro不仅会在shiro过滤器链下注册此过滤器还会在springmvc过滤器链上注册
所以我们还要让springmvc上的过滤器链上的此过滤器失效
4.让全局过滤器链上的JwtFilter失效
package com.qf.fmall.config;import com.qf.fmall.filter.JwtFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** 注意为了不让 自定义filter也被中注册到全局FitlterChain中* 需要添加如下配置类*/
Configuration
public class FilterConfig {AutowiredJwtFilter jwtFilter;Beanpublic FilterRegistrationBeanJwtFilter jwtFilterFilterRegistrationBean(){FilterRegistrationBeanJwtFilter registrationBean new FilterRegistrationBean();//要注册的filter的对象registrationBean.setFilter(jwtFilter);//让当前filter不用注册到全局过滤器链上registrationBean.setEnabled(false);return registrationBean;}}