springboot - vue - shiro 登录验证的一些坑

25 篇文章 0 订阅
16 篇文章 0 订阅

最近开发个人博客,shiro配置后,需要进行登录验证,如要输入用户名或密码不正确,需要进行异常拦截并将结果返回给前端,在此过程有一些坑,在此记录一下:

1. 前端登录界面

在这里插入图片描述
该界面是用vue开发的,登录的el-form:

      <!-- 登录表单区域 -->
      <el-form
        ref="loginFormRef"
        :model="loginForm"
        :rules="loginFormRules"
        label-width="0px"
        class="login_form"
      >
        <!-- 用户名 -->
        <el-form-item prop="username">
          <el-input v-model="loginForm.username" prefix-icon="iconfont icon-user"></el-input>
        </el-form-item>
        <!-- 密码 -->
        <el-form-item prop="password">
          <el-input
            v-model="loginForm.password"
            prefix-icon="iconfont icon-3702mima"
            type="password"
          ></el-input>
        </el-form-item>
        <!-- 按钮区域 -->
        <el-form-item class="btns">
          <el-button type="primary" @click="login">登录</el-button>
          <el-button type="info" @click="resetLoginForm">重置</el-button>
        </el-form-item>
      </el-form>

涉及到主要的函数:

  methods: {
    // 点击重置按钮,重置登录表单
    resetLoginForm() {
      // console.log(this);
      this.$refs.loginFormRef.resetFields()
    },
    login() {
      let result
      this.$refs.loginFormRef.validate(async (valid) => {
        if (!valid) return
        // 发送ajax请求登陆
        result = await reqPwdLogin(this.loginForm)
        console.log(result)


        if (result.status !== 200) return this.$message.error('登录失败!')
        this.$message.success('登录成功')
        // 1. 将登录成功之后的 token,保存到客户端的 sessionStorage 中
        //   1.1 项目中出了登录之外的其他API接口,必须在登录之后才能访问
        //   1.2 token 只应在当前网站打开期间生效,所以将 token 保存在 sessionStorage 中
        window.sessionStorage.setItem('token', result.data.token)
        // 2. 通过编程式导航跳转到后台主页,路由地址是 /home
         this.$router.push('/home')
      })
    },
  },

对应的axios函数:

 //登录操作,注意发达的post请求是json格式
 export function reqPwdLogin(loginForm){
  return  axios({
     method: 'post',
     url: `/login`,
     data: {
      username: loginForm.username,
      password: loginForm.password,
     },

  })
}

2.后端访问

2.1 controller层

   //登录
    @PostMapping("/api/login")
    public Object toLogin(@RequestBody JSONObject param) {
    	//vue传过来的一般是json数据,注意用@RequestBody接收
        //取出content
        String username = param.getString("username");
        String password=param.getString("password");

        //从SecurityUtils里边创建一个subject
        Subject subject = SecurityUtils.getSubject();
        //在认证提交前准备token(令牌)
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        //自定义返加的token,登录后自动分配一个线程Id
        String token_res = subject.getSession().getId().toString();
        Map<String,Object> map=new HashMap<>();
        map.put("token",token_res);

        //执行认证登陆,抛出的异常在GlobalExceptionHandler中处理
        subject.login(token);
        if (subject.isAuthenticated()) {
            return ApiResult.succ(map);

        } else {
            token.clear();
            return new ResponseEntity<>("登录失败", HttpStatus.NOT_FOUND);
        }
    }

2.2 CustomRealm中的验证操作

“subject.login(token)”为登录操作,为此需要进入自定义的CustomRealm中进行验证,具体shiro配置之前博文已写,这里不再赘述,CustomRealm验证函数如下:

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        System.out.println("=====身份认证======");
        //通过令牌拿到userName和userPwd
        String userName = (String) authenticationToken.getPrincipal();
        String userPwd = new String((char[]) authenticationToken.getCredentials());


        if (StringUtils.isEmpty(userName)) {
        	//定义UnknownAccountException异常,不过这里一般由前端验证,我没有对这个异常拦截
            throw new UnknownAccountException("用户名为空");
        }
        //根据用户名从数据库获取密码,加密方法见自定义的SimpleCredentialsMatcher子类,可以看我shiro配置博客。
        //查询数据库,判断该用户是否存在,userService具体定义很简单,这里不说了,可以自己实现
        User user = userService.queryByName(userName);
        if (user==null) {
        	//定义AccountException异常
            throw new AccountException("用户名不存在");
        }else{
            String password = user.getPassword();
            //SimpleAuthenticationInfo自动调用doCredentialsMatch进行加密匹配,可加断点追踪看加密后的值
            return new SimpleAuthenticationInfo(userName, password, getName());
        }
    }
}

这里我试过抛出自己定义的异常,但是CustomRealm中抛出的异常都属于AuthenticationException,自定义异常也会最终被识别为AuthenticationException,所以我自定义的异常拦截都不起作用,没有办法,只能用AuthenticationException的子类进行定义,AuthenticationException的子类有:
在这里插入图片描述

2.3 自定义异常拦截

    //======================处理shiro的Exception start======================================================

    /**
     * 处理 IncorrectCredentialsException
     */
    @ExceptionHandler(value = IncorrectCredentialsException.class)
    public ResponseEntity<Object> incorrectCredentialsException(IncorrectCredentialsException e) {
        // 打印堆栈信息
        log.error(ThrowableUtil.getStackTrace(e));
        return new ResponseEntity<>("密码错误", HttpStatus.NETWORK_AUTHENTICATION_REQUIRED);

    }

    /**
     * 处理 AuthenticationException  总异常
     */
    @ExceptionHandler(value = AuthenticationException.class)
    public ResponseEntity<String> authenticationException(AuthenticationException e) {
        // 打印堆栈信息
        log.error(ThrowableUtil.getStackTrace(e));
        return new ResponseEntity<>("登录验证失败", HttpStatus.NOT_FOUND);
    }

    /**
     * 处理 AccountException  用户名不存在
     */
    @ExceptionHandler(value = AccountException.class)
    public ResponseEntity<String> userNameNotFoundException(AccountException e) {
        // 打印堆栈信息
        log.error(ThrowableUtil.getStackTrace(e));
        return new ResponseEntity<>(e.getMessage(), NOT_FOUND);
    }


    //======================处理shiro请求的Exception end======================================================

3.前端response拦载

根据异常返回的response定义不同的消息框:

//response拦截器
axios.interceptors.response.use(
	//如果返回的status为200左右,则认为不是error
   response => {
      console.log("=====返回成功=====")
      return response.data

   },
   error => {
      //一般返回的status为300以上,则认为是error  
     let status=error.response.status;
     console.log(status)
     //对应自定义的IncorrectCredentialsException
     //HttpStatus.NETWORK_AUTHENTICATION_REQUIRED对应status为511
     if(status == 511){
      Message({
         type: 'warning',
         showClose: true,
         message: '密码错误'
      })
	//对应自定义的AccountException
	//HttpStatus.NOT_FOUND对应的status为404
     }else if(status==404){
         Message({
            type: 'warning',
            showClose: true,
            message: '用户名不存在'
         })

     }else{
         Message({
            type: 'warning',
            showClose: true,
            message: '连接超时'
         })
     }
  
      return Promise.reject('error')
   })

这里要说明一下,我把HttpStatus中大部分的value值都试了一下,一般new ResponseEntity封装的value值,也就是status为201,202,…都会认为是正常的response,如果为300几,400几,500几就会被认为是error。
在这里插入图片描述

4.用postman测一下

数据库中的用户值:
在这里插入图片描述
注意password都是加密后的值,原密码都是“123456”
先测一下正确的:
在这里插入图片描述
注意发送json格式的请求,按上图红框内配置,可以看到操作成功,并返回token。
再试试用户名不存在的:
在这里插入图片描述
再试试用户名存在,密码错误的:
在这里插入图片描述
成功!vue前端渲染显示我就不测了,只是用 Message组件封装并友好的回显而以。

5. 小结

我之前一直搞不懂ResponseEntity封装的response是如何被vue识别为error或者正常的,原来主要看respones中的status的数值进行判断。后面继续前进,如何对vue中的一些菜单操作进行授权和验证。如果对其他小伙伴有帮助,请记得给我点赞!谢谢

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SpringBoot是一款基于Java的开发框架,可用于快速搭建并开发Web应用程序。它提供了很多方便的功能和特性,例如自动配置、快速启动、依赖管理等,使得开发人员可以更加专注于业务逻辑的实现。 Shiro是一款轻量级的认证和授权框架,可以集成到SpringBoot应用中,用于用户身份验证、角色权限管理等安全相关的功能。Shiro提供了很多标准的安全特性,如身份验证、授权、加密、会话管理等,使得开发人员能够轻松地添加安全性到应用程序中。 Vue是一款流行的前端框架,用于构建用户界面。它使用了基于组件的开发方式,使得构建复杂的交互式页面变得更加简单和高效。Vue具有响应式的数据绑定、组件化开发、虚拟DOM等特性,提供了很多工具和功能,使得前端开发变得更加灵活和易于维护。 CAS(Central Authentication Service)是一种单点登录(SSO)协议,用于在多个应用系统中共享用户登录状态。CAS提供了一个中央认证服务,用户只需要在登录一次后,即可访问其他受信任的应用系统,无需再次输入用户名和密码。CAS可与SpringBootShiroVue等组件集成,实现单点登录功能,并提供了一些其他可选的功能,如用户个性化配置、会话管理、安全日志等。 通过将SpringBootShiroVue和CAS四者集成,可以构建一个完整的Web应用程序,具备安全性、高效性和用户友好性。SpringBoot提供了基础的开发框架,Shiro提供了安全认证和授权功能,Vue提供了良好的用户界面,而CAS提供了单点登录和用户会话管理等功能。这样的应用程序可以满足用户的安全性需求,提供友好的界面,同时保证了应用程序的高效运行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值