登录拦截器原理以及解决拦截器跨域问题
一.可以实现登录拦截的技术
filter(shiro) interceptor
filter:web的基础组件,功能相对简单
interceptor:springmvc组件,背靠spring容器,功能相对丰富,一般选用这个
1:web组件理解,有助于后续逻辑模块实现选择
2:比较多个组件优缺点,进行最实践的选中
拦截器原理
1.springmvc启动时候,扫描所有controller类,解析所有映射方法,
将每个映射方法封装一个对象HandlerMethod,该类包含所有请求映射方法信息(映射路径/方法名/参数/注解/返回值)
2.springmvc针对这些请求映射方法信息封装对象类 使用类似map的数据结构进行统一管理
Map<String,HandlerMethod> map
key:请求映射路径(url)
value就是映射方法信息封装对象类
map.put("/users/currentUser",currentUser这个映射方法对应HandlerMethod实例)
map.put("/users/login",login这个映射方法对应HandlerMethod实例)
3.页面发起请求时(/users/currentUser),进入拦截器之后,springmvc自动解析请求路径,
得到url(/users/currentUser),获取url之后,进而获取/users/currentUser 路径对应的映射方法HandlerMethod实例--handler
4.调用拦截器 preHandle方法并将请求对象,响应对象 映射方法对象handler一起传入
跨域请求流程
解决拦截器跨域问题(cors)
if (!(handler instanceof HandlerMethod)) {
return true;
}
二.有区别登录拦截(想到拦截器+自定义注解)
区分登录拦截实现流程
1:自定义一个注解:@RequiredLogin,约定如果请求映射方法贴了这个注解,表示访问这个请求必须先登录,不贴,表示无所谓
2:给注解赋予约定好的操作逻辑(在登录拦截器中实现约定逻辑)
1>判断当前访问的请求映射方法是否贴有@RequiredLogin标签
2>如果有表示当前映射方法需要登录校验
3>如果没有表示不需要校验直接放行
三.自定义注解(登录校验注解)
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequireLogin {
}
四.登录拦截(拦截器实现HandlerInterceptor)
public class CheckLoginInterceptor implements HandlerInterceptor {
@Autowired
private IUserInfoRedisService userInfoRedisService;
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod hm = (HandlerMethod) handler;
String token = request.getHeader("token");
UserInfo user = userInfoRedisService.getUserByToken(token);
if (hm.hasMethodAnnotation(RequireLogin.class)) {
if (user == null) {
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(JSON.toJSONString(JsonResult.noLogin()));
return false;
}
}
return true;
}
}
五.主配置类配置(拦截器配置以及解决跨域问题配置)
@SpringBootApplication
public class APP implements WebMvcConfigurer {
public static void main(String[] args) {
SpringApplication.run(APP.class, args);
}
@Bean
public CheckLoginInterceptor getCheckLoginInterceptor() {
return new CheckLoginInterceptor();
}
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getCheckLoginInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/regist")
.excludePathPatterns("/login")
.excludePathPatterns("/checkPhone")
.excludePathPatterns("/sendVerifyCode");
}
@Bean
public MappingMongoConverter mappingMongoConverter(MongoDbFactory factory, MongoMappingContext context, BeanFactory beanFactory) {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(factory);
MappingMongoConverter mappingConverter = new MappingMongoConverter(dbRefResolver, context);
try {
mappingConverter.setCustomConversions(beanFactory.getBean(CustomConversions.class));
} catch (NoSuchBeanDefinitionException ignore) {
}
// Don't save _class to mongo
mappingConverter.setTypeMapper(new DefaultMongoTypeMapper(null));
return mappingConverter;
}
//3.解决跨域问题
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
//重写父类提供的跨域请求处理的接口
public void addCorsMappings(CorsRegistry registry) {
//添加映射路径
registry.addMapping("/**")
.allowedOrigins("*")
//是否发送Cookie信息
.allowCredentials(true)
//放行哪些原始域(请求方式)
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
//放行哪些原始域(头部信息)
.allowedHeaders("*")
//暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
.exposedHeaders("Header1", "Header2");
}
};
}
}