解决一个粉丝反馈的spring-boot整合vue-element-admin项目出现的退出登录无效的Bug

引言

今年7-8月份的时候我连续发表了几篇spring-boot整合vue-element-admin项目实现自定义权限控制的实战文章,后面也受到了不少读者的点赞。后面自己也没注意去维护了,但是最近在CSDN上有一个粉丝还是一名高校教师突然给我留言说按照我的文章思路搭建了项目,也实现了自定义菜单和角色授权等功能。但是发现了一个很严重的bug,那就是退出登录无效,且退出登录后系统不会调到登录界面,点击其他页面也是一片空白。

csdn_wechat
图 1 读者反馈的项目退出登录失效bug

据这位读者反馈,这个问题困扰了他一周了都没法解决,于是只好留言向我求助。本着帮粉丝解决问题的同时也是在提升自己的想法,于是我重新启动项目开启了这个自己放在一边很久项目的调试之路,同时也参考了之前的luban-mall前后端分离项目,花了一个晚上终于把问题解决了,这一刻自己又找到了一丢丢的成就感!好久没更新公众号,顺便也把解决前后端分离且使用spring-security作为安全框架的场景下自定义退出登录的具体实现。

1 找到前端退出登录按钮的实现逻辑并定位问题

首先我们得找到实现退出登录按钮代码的地方,它在vue-element-admin项目的src/layout/components/Navbar.vue文件中logout方法中。

「修改前的逻辑」

async logout() {
    
    
      await this.$store.dispatch('user/logout')
      this.$router.push('/login')
 }

然后我们根据this.$store.dispatch('user/logout')这段代码找到src/store/user.js文件里actions里的logout行为方法,发现原来的退出登录逻辑里面是虽然调用后台退出登录接口,但是并没有走后台退出登录的逻辑。

logout({
     
      commit, state, dispatch }) {
    
    
    return new Promise((resolve, reject) => {
    
    
      logout(state.token).then(() => {
    
    
        commit('SET_TOKEN', '')
        commit('SET_ROLES', [])
        commit('SET_NAME', '')
        commit('SET_CURRENT_ROLE', null)
        window.sessionStorage.removeItem('userInfo')
        window.sessionStorage.removeItem('routeIds')
        window.sessionStorage.removeItem('roles')
        window.sessionStorage.removeItem('currentRole')
        removeToken()
        resetRouter()
        // reset visited views and cached views
        // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485
        dispatch('tagsView/delAllViews', null, {
    
     root: true })
        resolve()
      }).catch(error => {
    
    
        reject(error)
      })
    })
  }

每次在前端调试退出登录功能的时候,浏览器控制台中会报出一个如下所示的跨域失败的问题,导致交互压根到不了后台退出登录的接口

logout_error
图 2 前端退出登录时浏览器控制台报跨域失败

明明我在后端的spring-security配置类中是配置了跨域的,在这个前后端分离的项目中登录功能也是走的跨域完全没问题,为什么退出登录时还会报这样的跨域问题呢?这里就涉及到后台修改spring-security适配器类中的代码了

2 后台spring-security适配器类中禁用框架的退出登录

好在参考了我目前就职的公司里面spring-security配置类中的源码,发现需要在spring-security配置类中的禁用框架自带的退出登录。

WebSecurityConfig.java

这个spring-security的适配器类位于我的blogserver项目的src/main/java/org/sang/config目录下

@Override
    protected void configure(HttpSecurity http) throws Exception {
    
    
        // 配置跨域
        http.cors().configurationSource(corsConfigurationSource());
        // 禁用spring security框架的退出登录,使用自定义退出登录
        http.logout().disable();
        // 其他代码省略
    }

为了验证登录和退出登录的逻辑,我么分别在登录和退出登录的方法中都打上日志

UserService.java

这个类位于我的blogserver项目的src/main/java/org/sang/service目录下

user_login
图 3 后台项目中用户登录业务失效逻辑代码

UserController.java

这个类位于我的blogserver项目的src/main/java/org/sang/controller目录下

user_logout
图 4 后台项目自定义用户退出登录业务逻辑代码

退出登录方法中调用Util.clearContext()方法会在应用上下文中清空当前用户的认证信息

3 修改前端退出登录逻辑

后端禁用spring-security框架的退出登录后我们开始来修改前端退出登录逻辑

首先我们在前端vue-element-admin项目中的src/api/user.js文件中暴露一个退出登录的接口

export function logout() {
    
    
  return request({
    
    
    url: '/user/logout',
    method: 'post'
  })
}

然后在src/store/user.js文件中导入退出登录的接口并在退出登录行为方法中调用该接口

import {
    
     login, logout } from '@/api/user'
// 中间部分代码省略,需要查看完整代码的读者可前往代码仓库查看
const actions = {
    
    
// login({ commit }, userInfo) 省略
logout({
     
      commit, state, dispatch }) {
    
    
    return new Promise((resolve, reject) => {
    
    
      logout().then(res => {
    
    
        if (res.status === 200) {
    
    
          commit('SET_TOKEN', '')
          commit('SET_ROLES', [])
          commit('SET_NAME', '')
          commit('SET_CURRENT_ROLE', null)
          window.sessionStorage.removeItem('userInfo')
          window.sessionStorage.removeItem('routeIds')
          window.sessionStorage.removeItem('roles')
          window.sessionStorage.removeItem('currentRole')
          removeToken()
          resetRouter()
          // reset visited views and cached views
          // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485
          dispatch('tagsView/delAllViews', null, {
    
     root: true })
          Message.info('退出登录成功')
          // 下面这个resolve方法必须调用,否则如法到达Navbar组件中退出登录回调方法进行路由跳转到登录页面
          resolve()
        } else {
    
    
          reject('退出登录失败')
        }
      })
    })
  }
}

在后端退出登录成功后前端的cookie和session中我们也需要清空用户的缓存信息。

最后我们还要在Navbar.vue文件中的调用this.$store.dispatch('user/logout')方法的成功回调方法中刷新当前文档,并让当前路由重新进入登录界面。代码实现逻辑如下:

logout() {
    
    
      this.$store.dispatch('user/logout').then(res => {
    
    
        console.log(res)
        // 刷新当前文档
        location.reload()
        this.$router.push({
    
     path: '/login' })
      })
    }

以上逻辑与vue-element-vue项目中原作者的退出登录逻辑稍作了修改,这里我把异步方法改成了同步方法,并把刷新当前文档和重新进入登录界面的逻辑放在退出登录成功后的回调函数中实现

4 测试修改后的效果

修改前后端代码后我们就可以启动前后端项目进行测试,检验效果了!

我们登录成功之后进入项目主页面:

home_page
图 5 用户登录成功进入主页面

然后我们点击右上角的带有用户图标下面的小三角,在弹出的下拉框中点击Log Out按钮后页面会弹出“退出登录成功”的消息提示后当前页面进入登录界面,同时后台也打印了“退出登录”的日志

login_form
图 6 退出登录后当前页面进入登录界面

用户登录与退出登录过程后台控制台打印出的日志信息

2021-12-11 11:46:30.641  INFO 8496 --- [nio-8081-exec-1] org.sang.service.UserService             : 用户登录认证, username=heshengfu
2021-12-11 11:46:31.143  INFO 8496 --- [nio-8081-exec-3] o.s.controller.RouterResourceController  : roleId=1
2021-12-11 11:46:31.241  INFO 8496 --- [nio-8081-exec-4] o.s.controller.RouterResourceController  : roleId=1
2021-12-11 11:51:33.276  INFO 8496 --- [nio-8081-exec-9] org.sang.controller.UserController       : 退出登录

5 写在最后

好了,自定义退出登录的bug已经完成了解决。这里不得不说对于spring-security中的很多功能,自己尚未解锁,之前的学习还只是入了个门而已,远未达到熟练的地步。但是在已解决问题和交付为第一要务的前提下,我们在踩到有关spring-security的坑时最好去参考以下一些使用spring-security作为项目中的安全控制模块且技术比较成熟的开源项目,参考行业内的大牛是怎么使用spring-security实现各种定制的功能的。这样对于我们解决项目中遇到的bug必定会起到事半功倍的效果。关于spring-security的学习,这里我推荐公众号【江南一点雨】发布的SpringSecurity专辑文章,也可以购买他今年出版的热销技术书《深入浅出Spring Security》,也算是对作者长期耕耘java web技术栈的一种支持。

最后附上本文的前后端项目代码仓库地址,有需要查看项目完整实现的读者可以自己把源码clone下来自行研究,修改退出登录失效Bug后的代码也都已经提交到了我的个人gitee代码仓库。

后端项目blogserver项目gitee地址
前端vue-element-admin项目gitee地址

本文首发个人微信公众号,欢迎还没关注我的CSDN的粉丝读者扫描下面的微信二维码,加个公众号关注,我会定期更新实战干货文章,有问题大家也可以加问哦微信给我留言,看到留言后我也会尽快安排时间尽我所能帮助各位粉丝读者解决。

微信公众号

猜你喜欢

转载自blog.csdn.net/heshengfu1211/article/details/121885573