org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Unrecognized

最近写springSecurity +redis遇到问题报错

org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Unrecognized field "accountNonLocked" ]; line: 1, column: 18401] (through reference chain: JwtUser["accountNonLocked"])

在实现接口UserDetails过后报错,于是我查阅了大量的文章并且分析过源码,再加上一步一步的断点调试,总结出了以下结论

因为json反序列化原因UserDetails 这个里面对应getXXX(例如getPassword)的对象都要写对应的参数,否则json转义的时候会报错,如果用不到就增加注解@JsonIgnore。

对于权限Set集合因为json反序列化原因写入redis,再取出来会出现反序列化失败异常,所以此时我们应该写一个新的类来继承它让其序列化成功代码如下

package com.fan.security.authority;

/**
 * @author およそ神
 * @version JDK 1.8
 */

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
import org.springframework.util.Assert;

public class MyGrantedAuthority  implements GrantedAuthority {

    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

    private String authority;

    public MyGrantedAuthority() {
        super();
    }

    public MyGrantedAuthority(String authority) {
        Assert.hasText(authority, "A granted authority textual representation is required");
        this.authority = authority;
    }

    @Override
    public String getAuthority() {
        return authority;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (obj instanceof MyGrantedAuthority) {
            return authority.equals(((MyGrantedAuthority) obj).authority);
        }

        return false;
    }

    @Override
    public int hashCode() {
        return this.authority.hashCode();
    }

    @Override
    public String toString() {
        return this.authority;
    }
}

此时我们的UserDetailsService实现类如下

package com.fan.security;

import com.fan.security.authority.MyGrantedAuthority;
import com.fan.security.entity.SysPermission;
import com.fan.security.entity.SysUser;
import com.fan.security.service.SysPermissionService;
import com.fan.security.service.SysUserService;
import com.fan.security.vo.LoginUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author およそ神
 * @version JDK 1.8
 */
public class UserDetailsServiceImpl  implements UserDetailsService {
    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private SysPermissionService sysPermissionService;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //需要构造出 org.springframework.security.core.userdetails.User 对象并返回
        if (username == null || "".equals(username)) {
            throw new RuntimeException("用户不能为空");
        }
        //根据用户名查询用户
        SysUser sysUser = sysUserService.selectByName(username);
        if (sysUser == null) {
            throw new RuntimeException("用户不存在");
        }
        Set<MyGrantedAuthority> grantedAuthorities = new HashSet<>();
        if (sysUser != null) {
            //获取该用户所拥有的权限
            List<SysPermission> sysPermissions = sysPermissionService.selectListByUser(sysUser.getId());
            // 声明用户授权
            sysPermissions.forEach(sysPermission -> {
                MyGrantedAuthority grantedAuthority = new MyGrantedAuthority(sysPermission.getPermissionCode());
                grantedAuthorities.add(grantedAuthority);
            });
        }
        sysUser.setGrantedAuthorities(grantedAuthorities);
        return new LoginUser(sysUser);
    }

}

这样一来我们用redis的set方法存进去的时候再取出来就不会报错了

@PostMapping(value = "/login")
    public ResponseVO login(@RequestBody @Valid SysUserLoginDTO sysUserLoginDTO) {
        //1、创建一个登录令牌类(Authentication的实现类)  名字密码身份验证
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken
                        (sysUserLoginDTO.getAccount(), sysUserLoginDTO.getPassword());
        //2、  用户验证 调用登录
        Authentication authentication = null;
        //它就会去 调用你写的com.hxzy.service.impl.UserDetailsServiceImpl类的方法
        //传入需要验证的对象并调用验证方法赋值给authentication
        authentication = this.authenticationManager.authenticate(authenticationToken);
        if (authentication==null) throw new RuntimeException("登录失败,用户名或密码错误!");
        //authentication.isAuthenticated()-->验证是否成功返回true or false
        log.info("登录是否成功:{}",  authentication.isAuthenticated());
        //得到当前登录用户(本地线程池来做的,springsecurity框架做的)
        // 与JwtAuthenticationTokenFilter 类 关联在一起的          principal
        //获得被验证主题的身份强制转换成需要响应的tokenvo
        LoginUser loginUser=(LoginUser) authentication.getPrincipal();
        //生成把这个对象生成uuid,并且 把uuid变成jwt串
        String userId = loginUser.getUser().getId().toString();
        //创建新的令牌jwtStr
        String jwtToken = JwtUtils.createJWTToken(userId);
        SysUser user = loginUser.getUser();
        boolean b = redisUtils.set("login" + userId, user, 60L, TimeUnit.MINUTES);
        System.out.println("redis设置是否成功:"+b);
        //返回一个带数据的响应VO
        return ResponseVO.okHasData(jwtToken);
    }

最后顺利完成jwt认证

package com.fan.security.config;

import com.alibaba.fastjson.JSONObject;
import com.fan.security.entity.SysUser;
import com.fan.security.vo.LoginUser;
import com.fan.utils.jwt.JwtUtils;
import com.fan.utils.redis.RedisUtils;
import lombok.extern.slf4j.Slf4j;
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;

/**
 * @author およそ神
 * @version JDK 1.8
 */
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    RedisUtils redisUtils;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String token = request.getHeader("token");
        if (!StringUtils.hasText(token)) {
            //放行
            filterChain.doFilter(request, response);
            return;
        }
        //解析token
        String userId;
        try {userId = JwtUtils.getIdByJWTToken(token);}
        catch (Exception e) {throw new RuntimeException("token非法!");}

        //从redis中获取信息
        String redisKey = "login" + userId;
        SysUser loginUser = redisUtils.get(redisKey);
        if (Objects.isNull(loginUser)) throw new RuntimeException("用户未登录!");

        //重新登录一个springSecurity用户对象
        UsernamePasswordAuthenticationToken authenticationToken=new UsernamePasswordAuthenticationToken(loginUser,null, loginUser.getGrantedAuthorities());//TODO 权限未完成
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        //放行,继续做下一步操作
        filterChain.doFilter(request, response);
    }
}

你可能感兴趣的:(Java,经验知识,redis,json,security)