登录拦截器原理

登录拦截器原理以及解决拦截器跨域问题
一.可以实现登录拦截的技术
filter(shiro)  interceptor  
filter:web的基础组件,功能相对简单
interceptor:springmvc组件,背靠spring容器,功能相对丰富,一般选用这个
1:web组件理解,有助于后续逻辑模块实现选择
2:比较多个组件优缺点,进行最实践的选中

登录拦截器原理_第1张图片

拦截器原理
//handler变量是这个HandlerMethod的实例对象
//HandlerMethod 请求映射方法信息(所在类的信息,方法信息[映射路径/方法名/参数/注解/返回值]...)封装对象

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一起传入
跨域请求流程

登录拦截器原理_第2张图片

解决拦截器跨域问题(cors)
//解决跨域问题
	//1.如果请求不是动态的,handler对象不是HandlerMethod的实例(静态页面).放行
	//2.如果请求是跨域请求(请求方法是:OPTIONS),handler对象不是HandlerMethod,
    //如果请求是跨域请求(请求方法是:OPTIONS),handler对象不是HandlerMethod当发生跨域时,
	//系统会先发送一条预请求,请求路径与真实请求一样,但是请求方式为OPTIONS它不属于handler实例,
	//如果我们不写下面这个判断,它会继续后面的操作,但是由于预请求没有携带参数,
	//在后面的判断中就会被直接拦截下来,这样导致这个请求失败
        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 {
        //解决跨域问题
        //如果请求是跨域请求(请求方法是:OPTIONS),handler对象不是HandlerMethod
        //当发生跨域时,系统会先发送一条预请求,请求路径与真实请求一样,但是请求方式为OPTIONS
        //它不属于handler实例,如果我们不写下面这个判断,它会继续进行下面的操作,但是由于预请求没有
        //携带参数,在下面的判断中就会被直接拦截下来,这样导致这个请求失败
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        //只要贴了@RequireLogin这个注解,就需要将该方法拦截下来并判断是否登录过
        //获取处理方法
        HandlerMethod hm = (HandlerMethod) handler;
        //贴在外面保证有效时间可以重置
        String token = request.getHeader("token");
        UserInfo user = userInfoRedisService.getUserByToken(token);
        //判断hm方法中是否包含@ReqireLogin该注解
        if (hm.hasMethodAnnotation(RequireLogin.class)) {
            //通过token查询用户的操作,需要查询操作放到判断有木有该@ReqireLogin注解之前
            //如果放在里面,会导致其他已登录的没有的贴该RequireLogin注解的有效时间没有重置,
           /* String token = request.getHeader("token");
            UserInfo user = userInfoRedisService.getUserByToken(token);*/
            //通过token获取user,并判断是否登录,查询出来有用户则表示登录过
            if (user == null) {
                //没有登录就设置编码格式,并返回json格式数据
                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();
    }

//1.让拦截器生效,并不拦截一些请求
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(getCheckLoginInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/regist")
                .excludePathPatterns("/login")
                .excludePathPatterns("/checkPhone")
                .excludePathPatterns("/sendVerifyCode");
    }

//2.mongodb去除_class
    @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");
            }
        };
    }
}

你可能感兴趣的:(java,spring,web,interceptor)