SpringSecurity登录报错403问题:com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field异常、及 Jackson 的使用

  项目使用SpringSecurity进行安全管理,之前登录接口都是好好的,今天突然验证码登录一直报403,最后发现问题所在,记录一下。

一、问题背景与解决方案

  因为前端登录注册使用差不多相同的流程,所以使用了 mixins ,表单对象如下:

loginForm: {
  phoneNum: '',
  password: '',
  repeatPassword: '',  // 最早是没有这个字段的,因为做忘记密码,所以也采用了这个mixin,然后加了这个字段
  code: ''
},

  问题就出在加的那个字段上,后台SpringSecurity的拦截器上获取请求参数采用的这个方法

// 登录是否含手机号
User voUser = new ObjectMapper().readValue(req.getInputStream(), User.class);
if (voUser == null || voUser.getPhoneNum() == null) {
    throw new AuthenticationServiceException("请输入手机号");
}

  而我的 User.class 之前没做忘记密码之前,实体类也没加这个字段

private String repeatPassword;

  所以导致匹配不上这个类,所以报错:com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "repeatPassword"

  解决方案就是:将 repeatPassword 这个字段在实体类上加上即可。

  但是肯定不能这么简单就了事,本着遇到问题就学习的精神,所以把这个 new ObjectMapper().readValue() 这个东西研究一下先。

二、Jackson 介绍

  new ObjectMapper().readValue() 这个东西来源于 jackson。

1、jackson是什么

  Java下常见的Json类库有Gson、JSON-lib和Jackson等,Jackson相对来说比较高效,在项目中主要使用Jackson进行JSON和Java对象转换,下面给出一些Jackson的JSON操作方法。

2、准备工作

  首先去官网下载Jackson工具包,下载地址http://wiki.fasterxml.com/JacksonDownload。Jackson有1.x系列和2.x系列,2.x系列有3个jar包需要下载:

jackson-core-2.2.3.jar
jackson-annotations-2.2.3.jar
jackson-databind-2.2.3.jar

  但是如果使用SpringBoot就很方便啦,不需要这步,因为springboot默认配置的 json 转换工具就是 jackson。

3、JSON序列化  —— JAVA对象转JSON

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
 
import com.fasterxml.jackson.databind.ObjectMapper;
 
public class JacksonDemo {
    public static void main(String[] args) throws ParseException, IOException {
        User user = new User();
        user.setName("小民");    
        user.setEmail("xiaomin@sina.com");
        user.setAge(20);
        
        SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd");
        user.setBirthday(dateformat.parse("1996-10-01"));        
        
        /**
         * ObjectMapper是JSON操作的核心,Jackson的所有JSON操作都是在ObjectMapper中实现。
         * ObjectMapper有多个JSON序列化的方法,可以把JSON字符串保存File、OutputStream等不同的介质中。
         * writeValue(File arg0, Object arg1)把arg1转成json序列,并保存到arg0文件中。
         * writeValue(OutputStream arg0, Object arg1)把arg1转成json序列,并保存到arg0输出流中。
         * writeValueAsBytes(Object arg0)把arg0转成json序列,并把结果输出成字节数组。
         * writeValueAsString(Object arg0)把arg0转成json序列,并把结果输出成字符串。
         */
        ObjectMapper mapper = new ObjectMapper();
        
        //User类转JSON
        //输出结果:{"name":"小民","age":20,"birthday":844099200000,"email":"xiaomin@sina.com"}
        String json = mapper.writeValueAsString(user);
        System.out.println(json);
        
        //Java集合转JSON
        //输出结果:[{"name":"小民","age":20,"birthday":844099200000,"email":"xiaomin@sina.com"}]
        List<User> users = new ArrayList<User>();
        users.add(user);
        String jsonlist = mapper.writeValueAsString(users);
        System.out.println(jsonlist);
    }
}

  ObjectMapper是JSON操作的核心,Jackson的所有JSON操作都是在ObjectMapper中实现。

  ObjectMapper有多个JSON序列化的方法,可以把JSON字符串保存File、OutputStream等不同的介质中。

(1)writeValue(File arg0, Object arg1)把arg1转成json序列,并保存到arg0文件中。

(2)writeValue(OutputStream arg0, Object arg1)把arg1转成json序列,并保存到arg0输出流中。

(3)writeValueAsBytes(Object arg0)把arg0转成json序列,并把结果输出成字节数组。

(4)writeValueAsString(Object arg0)把arg0转成json序列,并把结果输出成字符串。

4、JSON反序列化 —— JSON转Java类

import java.io.IOException;
import java.text.ParseException;
import com.fasterxml.jackson.databind.ObjectMapper;
 
public class JacksonDemo {
    public static void main(String[] args) throws ParseException, IOException {
        String json = "{"name":"小民","age":20,"birthday":844099200000,"email":"xiaomin@sina.com"}";
        
        /**
         * ObjectMapper支持从byte[]、File、InputStream、字符串等数据的JSON反序列化。
         */
        ObjectMapper mapper = new ObjectMapper();
        User user = mapper.readValue(json, User.class);
        System.out.println(user);
    }
}

  ObjectMapper支持从byte[]、File、InputStream、字符串等数据的JSON反序列化。主要采用的就是 readValue() 方法。

5、Jackson 注解

  Jackson提供了一系列注解,方便对JSON序列化和反序列化进行控制,下面介绍一些常用的注解。

@JsonIgnore 此注解用于属性上,作用是进行JSON操作时忽略该属性。

@JsonFormat 此注解用于属性上,作用是把Date类型直接转化为想要的格式,如@JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss")。

@JsonProperty 此注解用于属性上,作用是把该属性的名称序列化为另外一个名称,如把trueName属性序列化为name,@JsonProperty("name")。

// 比如我们常用的时间格式化注解,就是这个 jackson 里的啦
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", locale = "zh", timezone = "GMT+8")
private Date createTime;
// 不JSON序列化年龄属性
@JsonIgnore 
private Integer age;

// 序列化email属性为mail
@JsonProperty("mail")
private String email;

6、自定义配置文件

#jackson
#日期格式化
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
#spring.jackson.date-format=yyyy-MM-dd
#格式化输出 spring.jackson.serialization.indent_output=true
#忽略无法转换的对象 spring.jackson.serialization.fail_on_empty_beans=false
#设置空如何序列化 spring.jackson.defaultPropertyInclusion=NON_EMPTY
#允许对象忽略json中不存在的属性 spring.jackson.deserialization.fail_on_unknown_properties=false
#允许出现特殊字符和转义符 spring.jackson.parser.allow_unquoted_control_chars=true
#允许出现单引号 spring.jackson.parser.allow_single_quotes=true

  加了这个配置,spring.jackson.date-format=yyyy-MM-dd HH:mm:ss,我们就不需要再在实体类里去加这样的注解了。

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", locale = "zh", timezone = "GMT+8")
private Date createTime;

  注意时区是必须要加的,上面配置没有加时区

#jackson相关配置
spring.jackson.date-format = yyyy-MM-dd HH:mm:ss
#时区必须要设置 spring.jackson.time-zone= GMT+8
#ALWAYS的意思是即时属性为null,仍然也会输出这个key spring.jackson.default-property-inclusion=ALWAYS

  yml形式

spring:
    jackson:
       #参数意义:
       #JsonInclude.Include.ALWAYS              默认
       #JsonInclude.Include.NON_DEFAULT     属性为默认值不序列化
       #JsonInclude.Include.NON_EMPTY         属性为 空(””) 或者为 NULL 都不序列化
       #JsonInclude.Include.NON_NULL           属性为NULL   不序列化
       default-property-inclusion: ALWAYS
       time-zone: GMT+8
       date-format: yyyy-MM-dd HH:mm:ss

7、创建bean、及在配置类中修改属性,可以看这篇博客:https://blog.csdn.net/weixin_38413579/article/details/82562634

三、继续解决方案

  然后我们继续看问题的解决方案。

1、第一种解决方案

  ObjectMapper 对象添加:mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

String s = "{"id":1,"name":"test","age":19}";
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
User value = mapper.readValue(s, User.class);
System.out.println(value);

2、第二种解决方案

  在需要转化的对象的类中添加注解,注解信息如下

@JsonIgnoreProperties(ignoreUnknown = true)
public class User{
     ......
}

3、第三种解决方案,就是上面的在实体类上加上对应的字段

原文地址:https://www.cnblogs.com/goloving/p/14921674.html