学习登录接口过程中的踩坑记录

学习视频地址:SpringSecurity框架教程-Spring Security+JWT实现项目级前端分离认证授权-B站最通俗易懂的Spring Security课程

🟥异常1️⃣: com.alibaba.fastjson.JSONObject and com.demo.entity.LoginUser are in unnamed module of loader 'app'

异常位置

JwtAuthenticationTokenFilter 类 -> doFilterInternal 方法 -> LoginUser loginUser = redisCache.getCacheObject(redisKey);

package com.demo.filter;

import com.alibaba.fastjson.JSONObject;
import com.demo.constant.JwtConstant;
import com.demo.entity.LoginUser;
import com.demo.util.JwtUtil;
import com.demo.util.RedisCache;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;

/**
 * Token 认证过滤器
 *
 * @author action
 * @date 2022/11/8 22:23
 */
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private RedisCache redisCache;


    /**
     * 内部过滤器
     *
     * @param request     请求
     * @param response    响应
     * @param filterChain 过滤链
     * @throws ServletException Servlet异常
     * @throws IOException      IO异常
     * @date 2022/11/8 22:36
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 获取Token
        String token = request.getHeader("token");
        if (!StringUtils.hasText(token)) {
            // token不存在, 接口放行并返回
            filterChain.doFilter(request, response);
            return;
        }

        String userId;
        // 解析Token
        try {
            Claims claims = JwtUtil.parseJwt(token);
            userId = claims.getSubject();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(JwtConstant.ILLEGAL_TOKEN);
        }


        // 从Redis缓存中获取用户信息
        String redisKey = "login:" + userId;

        LoginUser loginUser = redisCache.getCacheObject(redisKey);

        if (Objects.isNull(loginUser)) {
            throw new RuntimeException(JwtConstant.NOT_SIGN_IN);
        }

        // 存入 SecurityContextHolder
        // TODO 封装权限信息到 authentication
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, null);
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

        // 全部处理完成后放行
        filterChain.doFilter(request, response);
    }
}

异常原因分析

参考文章:解决FastJson com.alibaba.fastjson.JSONObject cannot be cast to的问题
找到原因:此处从redis中获取的 redisCache 对象无法通过 fastJson 强转为 LoginUser 对象

起初我以为存入 Redis 的 loginUser 对象格式有问题,但是不管怎么存都是这样的格式:

{
  "@type": "com.demo.entity.LoginUser",
  "accountNonExpired": true,
  "accountNonLocked": true,
  "credentialsNonExpired": true,
  "enabled": true,
  "password": "$2a$10$TXWXQbE7TFmUuV",
  "user": {
    "avatar": "",
    "createTime": 1651143735000,
    "email": "xxxxx@qq.com",
    "enable": 1,
    "expired": 0,
    "id": 1,
    "locked": 1,
    "nickName": "某某",
    "password": "$2a$10$TXWXQbE7TFmUuV",
    "phone": "123",
    "sex": 1,
    "updateTime": 1667905073651,
    "username": "lee"
  },
  "username": "lee"
}

🤨直到我翻视频下的评论看到
评论区截图
果然评论区出人才啊!确实是 fastjson 版本高
视频中Up主用的是:1.2.33 而我用的是: 2.0.14
新版本不支持强转

参考文章:阿里巴巴fastjson版本从1.2.72升级到2.0.14。开启autoType,不支持Object对象接收强转成Java对象的解决方案

异常解决方案

LoginUser loginUser = redisCache.getCacheObject(redisKey) 替换为如下:

// 先转成JSON对象
JSONObject jsonObject = redisCache.getCacheObject(redisKey);
// JSON对象转换成Java对象
LoginUser loginUser = jsonObject.toJavaObject(LoginUser.class);

🟥异常2️⃣com.alibaba.fastjson2.JSONException: invoke constructor error, public com.demo.entity.LoginUser(com.demo.entity.User)

异常位置

JwtAuthenticationTokenFilter 类 -> doFilterInternal 方法 -> LoginUser loginUser = jsonObject.toJavaObject(LoginUser.class);

异常原因分析

调用LoginUser 实体类的构造函数错误

UP主视频中的 LoginUser 类用的是 loombok 依赖代替写某些方法

  • @NoArgsConstructor 代替写无参构造方法
  • @AllArgsConstructor 代替写有参构造方法
  • @Data 代替写 get 和 set 方法

我自己写的实体类 LoginUser 如下:

package com.demo.entity;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;

/**
 * 登录用户类
 *
 * @author Actio
 * @date 2022/10/13 1:58
 */
public class LoginUser implements UserDetails {
    private User user;

	 /**
     * 有参构造方法
     */
    public LoginUser(User user) {
        this.user = user;
    }

    public String toString() {
        return "LoginUser(user=" + getUser() + ")";
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

很显然,我没有用 loombok 依赖,自己手动写了上述方法,然后就出事了 😵‍💫

异常解决方案

参考文章:有关Java实体类中get、set方法和有参无参构造方法的个人见解。

总的来说,get和set方法就是为了能以得到和设置实体类中的私有属性值,而一个类中至少要有一个构造方法,
当没有人为添加的时候,编译器会自动加入一个隐式的无参构造方法,
当有人为添加时,编译器就不会自动添加了。
无参构造方法的作用是为了比较方便的new出一个对象

✍️ 实体类中如果未使用 loombok 依赖或插件自己写了有参构造方法时:请务必写上无参构造方法!

	 /**
     * 无参构造方法
     */
    public LoginUser() {
        super();
    }

至于为什么不想用 loombok 插件,原因请自行百度~

综上,受益良多!😼

ps:如有错误,欢迎批评指正,谢谢!

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

leeindex

感谢您的鼓励~~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值