0


springboot集成shiro+前端vue,前后端分离项目遇到跨域以及sessionid拿不到等问题

近期在写前后端分离的项目,由于前后端分离导致原来使用的shiro配置无法满足现有系统要求。同时在前后端分离项目中存在的一些问题。例如,一些用户信息需要存储在后端方便进行安全性判断,但这些存储在后端的session前端却获取不到(特别奇怪),以及浏览器访问后端接口出现的跨域问题。

1、跨域问题

由于前后端分离导致前端和后端分别占用不同的端口,所以浏览器在访问不同接口的时候就会存在跨域问题。

解决方法

我是在springboot后端项目中添加CorsConfig配置类,用于解决跨域问题,当然前端也可以解决这个跨域问题。

importorg.springframework.boot.web.servlet.FilterRegistrationBean;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.web.cors.CorsConfiguration;importorg.springframework.web.cors.UrlBasedCorsConfigurationSource;importorg.springframework.web.filter.CorsFilter;importorg.springframework.web.servlet.config.annotation.CorsRegistry;importorg.springframework.web.servlet.config.annotation.WebMvcConfigurer;importorg.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;@Configurationpublicclass corsConfig {@BeanpublicWebMvcConfigurerCORSConfigurer(){returnnewWebMvcConfigurerAdapter(){@OverridepublicvoidaddCorsMappings(CorsRegistry registry){
                registry.addMapping("/**").allowedOrigins("*").allowedMethods("*").allowedHeaders("*")//设置是否允许跨域传cookie.allowCredentials(true)//设置缓存时间,减少重复响应.maxAge(3600);}};}@BeanpublicFilterRegistrationBeancorsFilter(){finalUrlBasedCorsConfigurationSource source =newUrlBasedCorsConfigurationSource();finalCorsConfiguration config =newCorsConfiguration();// 允许cookies跨域
        config.setAllowCredentials(true);// #允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin
        config.addAllowedOrigin("*");// #允许访问的头信息,*表示全部
        config.addAllowedHeader("*");// 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
        config.setMaxAge(3600L);// 允许提交请求的方法,*表示全部允许
        config.addAllowedMethod("OPTIONS");
        config.addAllowedMethod("HEAD");
        config.addAllowedMethod("GET");
        config.addAllowedMethod("PUT");
        config.addAllowedMethod("POST");
        config.addAllowedMethod("DELETE");
        config.addAllowedMethod("PATCH");
        source.registerCorsConfiguration("/**", config);FilterRegistrationBean bean =newFilterRegistrationBean(newCorsFilter(source));// 设置监听器的优先级
        bean.setOrder(0);return bean;}}

这样就能解决前后端跨域问题。

2、后端存储的session,前端获取不到

起因是我在做登录的时候,用户信息被我用session存储在了后端,但是我在调用其他接口的时候,我却无法在后端获取到我保存在后端的用户信息,无法判断前端是否登录过。

这个问题一开始困扰了我很久,搜了半天发现了一个不错的解释:

这是因为服务器判断前端的请求是同一个 session 的依据是通过网页默认的一个sessionid的cookie判断的,如果存在跨域,cookie值传不过来,也就当下一个请求过来时服务端无法识别为同一个会话,会被当做一个新的会话处理,故找不到session原保存的值。

在这里插入图片描述
所以我就去测试两个不同请求的sessionId是否是一样的,发现果然不一样,这也是为什么我在后端无法获取到我保存在后端的session。

解决方法

其实解决办法有很多种,例如说不用session存储数据而是利用Redis,这当然是最轻松的方式。但是本着我的session是利用shiro自带的Util存储的,所以我的解决方式是将sessionId回传给前端,然后前端保存后每次访问后端接口的时候携带保存的sessionId,这样就能我就能获取到原来的session了。

1、在登录接口中将sessionId回传给前端

publicResponseResultdoLogin(String username,String password){UsernamePasswordToken token =newUsernamePasswordToken(username,password);Subject subject =SecurityUtils.getSubject();try{
        subject.login(token);UserInfo userInfo = baseMapper.selectById(username);//利用shiro的Util获取sessionIdString sessionId =(String) subject.getSession().getId();//存储用户信息ShiroUtils.setSessionAttribute(SessionConst.USER_INFO_SESSION,userInfo);System.out.println((String)ShiroUtils.getSession().getId());
        
        userInfo.setSalt("");
        userInfo.setPassword("");Map<String,Object> data =newHashMap<>();//保存key-value类型数据AUTHORIZATION,值为sessionId
        data.put("AUTHORIZATION", sessionId);
        data.put("userInfo",userInfo);//回传给前端returnResponseResult.success(data);}catch(UnknownAccountException e){returnResponseResult.error(ResponseResultEnum.SESSION_ERROR);}catch(IncorrectCredentialsException e){returnResponseResult.error(ResponseResultEnum.PASSWORD_ERROR);}}

前端拿到sesionId后进行保存,并每次调用接口的时候携带Id即可

//举例:登录methodlogin(){this.$axios.get("/login",{params:{username:this.form1Data.username,password:this.form1Data.password
        }}).then((res)=>{if(res.data.code ===200){
            console.log(res)var userInfo = res.data.data.userInfo
            var auth = res.data.data.AUTHORIZATION
            ElementUI.Message.success("登录成功");this.$store.commit("SET_USERINFO", userInfo);//将信息保存在auth中this.$store.commit("SET_AUTH", auth)let url_name =this.$route.params.redirect
            console.log(url_name)if(url_name !==undefined&& url_name !==null&& url_name !=''){this.$router.replace({name: url_name
                })}elsethis.$router.push('/')}else{// 有问题
            ElementUI.Message.error(res.data.message);}})}//在main.js文件中进行前置拦截
axios.interceptors.request.use(config=>{let auth = store.getters.get_auth
    //从auth中获取后端传给前端的sessionId,以后调用任何接口就可以携带sessionId了if(auth !=null){
        config.headers['AUTHORIZATION']= auth;}
    console.log(config.headers)return config;},error=>{return Promise.reject(error)});

2、在配置类中写一个自定义的

sessionManager

并重写

getSessionId

方法,便于对前端传递过来的sessionId进行判断,检测是否是原来的sessionId。

@Configuration@Slf4jpublicclassMySessionManagerextendsDefaultWebSessionManager{privatestaticfinalStringREFERENCED_SESSION_ID_SOURCE="Stateless request";publicMySessionManager(){super();}@OverrideprotectedSerializablegetSessionId(ServletRequest request,ServletResponse response){String id =WebUtils.toHttp(request).getHeader("AUTHORIZATION");//如果请求头中有 Authorization (前端请求头中设置的名字)则其值为sessionIdif(!StringUtils.isEmpty(id)){
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,REFERENCED_SESSION_ID_SOURCE);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID,Boolean.TRUE);return id;}else{//否则按默认规则从cookie取sessionIdreturnsuper.getSessionId(request, response);}}}

3、在shiro配置类中注入自定义的session缓存管理器

@Bean("sessionManager")publicSessionManagersessionManager(){MySessionManager sessionManager =newMySessionManager();
    sessionManager.setSessionDAO(newEnterpriseCacheSessionDAO());
    sessionManager.setGlobalSessionTimeout(1000*60*30);
    sessionManager.setSessionValidationSchedulerEnabled(true);
    sessionManager.setSessionIdUrlRewritingEnabled(false);return sessionManager;}

关键: 因为

getSessionId()

方法是通过

DefaultWebSecurityManager类

进行实现的,所以我们需要将

SessionManager

注入到安全管理中

/**
     * 安全管理
     *
     * @return
     */@BeanpublicDefaultWebSecurityManagersecurityManager(){DefaultWebSecurityManager securityManager =newDefaultWebSecurityManager();//注入
    securityManager.setSessionManager(sessionManager());
    securityManager.setRealm(getShiroRealm());return securityManager;}

至此可解决前后端跨域后的sessionId的问题。

标签: spring boot vue

本文转载自: https://blog.csdn.net/qq_50595984/article/details/135628309
版权归原作者 听弧丶 所有, 如有侵权,请联系我们删除。

“springboot集成shiro+前端vue,前后端分离项目遇到跨域以及sessionid拿不到等问题”的评论:

还没有评论