安全性在REST API开发中扮演着重要的角色。一个不安全的REST API可以直接访问到后台系统中的敏感数据。因此,企业组织需要关注API安全性。
Spring Security 提供了各种机制来保护我们的 REST API。其中之一是 API 密钥。API 密钥是客户端在调用 API 调用时提供的令牌。
在本教程中,我们将讨论如何在Spring Security中实现基于API密钥的身份验证。
Spring Security可以用来保护REST API的安全性。REST API是无状态的,因此不应该使用会话或cookie。相反,应该使用Basic authentication,API Keys,JWT或OAuth2-based tokens来确保其安全性。
Basic authentication是一种简单的认证方案。客户端发送HTTP请求,其中包含Authorization标头的值为Basic base64_url编码的用户名:密码。Basic authentication仅在HTTPS / SSL等其他安全机制下才被认为是安全的。
OAuth2是REST API安全的行业标准。它是一种开放的认证和授权标准,允许资源所有者通过访问令牌将授权委托给客户端,以获得对私有数据的访问权限。
一些REST API使用API密钥进行身份验证。API密钥是一个标记,用于向API客户端标识API,而无需引用实际用户。标记可以作为查询字符串或在请求头中发送。
让我们首先在我们的pom.xml中声明spring-boot-starter-security依赖关系:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
实现思路是从请求头中获取API Key,然后使用我们的配置检查秘钥。在这种情况下,我们需要在Spring Security 配置类中添加一个自定义的Filter。
我们将从实现GenericFilterBean开始。GenericFilterBean是一个基于javax.servlet.Filter接口的简单Spring实现。
让我们创建AuthenticationFilter类:
public class AuthenticationFilter extends GenericFilterBean { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { try { Authentication authentication = AuthenticationService.getAuthentication((HttpServletRequest) request); SecurityContextHolder.getContext().setAuthentication(authentication); } catch (Exception exp) { HttpServletResponse httpResponse = (HttpServletResponse) response; httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); httpResponse.setContentType(MediaType.APPLICATION_JSON_VALUE); PrintWriter writer = httpResponse.getWriter(); writer.print(exp.getMessage()); writer.flush(); writer.close(); } filterChain.doFilter(request, response); } }
我们只需要实现doFilter()方法,在这个方法中我们从请求头中获取API Key,并将生成的Authentication对象设置到当前的SecurityContext实例中。
然后请求被传递给其余的过滤器处理,接着转发给DispatcherServlet最后到达我们的控制器。
在AuthenticationService类中,实现从Header中获取API Key并构造Authentication对象,代码如下:
public class AuthenticationService { private static final String AUTH_TOKEN_HEADER_NAME = "X-API-KEY"; private static final String AUTH_TOKEN = "Baeldung"; public static Authentication getAuthentication(HttpServletRequest request) { String apiKey = request.getHeader(AUTH_TOKEN_HEADER_NAME); if ((apiKey == null) || !apiKey.equals(AUTH_TOKEN)) { throw new BadCredentialsException("Invalid API Key"); } return new ApiKeyAuthentication(apiKey, AuthorityUtils.NO_AUTHORITIES); } }
在这里,我们检查请求头是否包含 API Key,如果为空 或者Key值不等于密钥,那么就抛出一个 BadCredentialsException。如果请求头包含 API Key,并且验证通过,则将密钥添加到安全上下文中,然后调用下一个安全过滤器。getAuthentication 方法非常简单,我们只是比较 API Key 头部和密钥是否相等。
为了构建 Authentication 对象,我们必须使用 Spring Security 为了标准身份验证而构建对象时使用的相同方法。所以,需要扩展 AbstractAuthenticationToken 类并手动触发身份验证。
为了成功地实现我们应用的身份验证功能,我们需要将传入的API Key转换为AbstractAuthenticationToken类型的身份验证对象。AbstractAuthenticationToken类实现了Authentication接口,表示一个认证请求的主体和认证信息。
让我们创建ApiKeyAuthentication类:
public class ApiKeyAuthentication extends AbstractAuthenticationToken { private final String apiKey; public ApiKeyAuthentication(String apiKey, Collection<?extends GrantedAuthority> authorities) { super(authorities); this.apiKey = apiKey; setAuthenticated(true); } @Override public Object getCredentials() { return null; } @Override public Object getPrincipal() { return apiKey; } }
ApiKeyAuthentication 类是类型为 AbstractAuthenticationToken 的对象,其中包含从 HTTP 请求中获取的 apiKey 信息。在构造方法中使用 setAuthenticated(true) 方法。因此,Authentication对象包含 apiKey 和authenticated字段:
通过创建建一个SecurityFilterChain bean,可以通过编程方式把我们上面编写的自定义过滤器(Filter)进行注册。
我们需要在 HttpSecurity 实例上使用 addFilterBefore() 方法在 UsernamePasswordAuthenticationFilter 类之前添加 AuthenticationFilter。
创建SecurityConfig 类:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf() .disable() .authorizeRequests() .antMatchers("/**") .authenticated() .and() .httpBasic() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .addFilterBefore(new AuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } }
此外注意代码中我们吧绘画策略(session policy)设置为无状态(STATELESS),因为我们使用的是REST。
最后,我们创建ResourceController,实现一个Get请求 /home
@RestController public class ResourceController { @GetMapping("/home") public String homeEndpoint() { return "Baeldung !"; } } 3.6. 禁用 Auto-Configuration @SpringBootApplication(exclude = {SecurityAutoConfiguration.class, UserDetailsServiceAutoConfiguration.class}) public class ApiKeySecretAuthApplication { public static void main(String[] args) { SpringApplication.run(ApiKeySecretAuthApplication.class, args); } }
我们先不提供API Key进行测试
curl --location --request GET 'http://localhost:8080/home'
返回 401 未经授权错误。
请求头中加上API Key后,再次请求
curl --location --request GET 'http://localhost:8080/home' \ --header 'X-API-KEY: Baeldung'
请求返回状态200
#centos #安装gcc yum install gcc-c++ #安装PCRE pcre-devel yum install -y pcre pcre-devel #安装zlib yum install -y zlib zlib-devel #安装Open SSL yum install -y openssl openssl-devel #ubuntu sudo apt update sudo apt-get install libpcre3-dev sudo apt-get install ruby sudo apt-get install zlib1g-dev
wget http://nginx.org/download/nginx-1.23.0.tar.gz
你也可以到网站选择你所需要的版本
下载完成后将压缩包通过ssh命令使用winscp工具或其他工具上传至服务器
#创建一个文件夹 cd /usr/local mkdir nginx cd nginx #解压缩包,直接tar -xvf nginx安装包所在路径 tar -xvf nginx-1.16.1.tar.gz
#进入nginx目录 cd /usr/local/nginx #进入目录 cd nginx-1.16.1 #编译 执行命令 考虑到后续安装ssl证书 添加两个模块 如不需要直接执行./configure即可 ./configure --with-http_stub_status_module --with-http_ssl_module #执行make命令(要是执行不成功请检查最开始安装的四个依赖有没有安装成功) make #执行make install命令 make install
补充查看编译参数
# 查看编译参数 ./configure --help | more
cd /usr/local/nginx/sbin # 默认配置文件启动 ./nginx # 指定配置文件启动 ./nginx -c /usr/local/nginx/conf/nginx.conf
cd /usr/local/nginx/sbin # 停止指令 ./nginx -s stop # 或 ./nginx -s quit # 重启命令 ./nginx -s reload # 查看nginx进程 ps -ef|grep nginx
#编辑 vim /etc/rc.local #最底部增加这一行 /usr/local/nginx/sbin/nginx
若要修改监听端口,可打开nginx的配置文件进行修改。
# 打开配置文件 vi /usr/local/nginx/conf/nginx.conf
将端口号改成8089(随便挑个端口,你也可以修改为8848,等等)。
若想使用外部主机访问nginx,上一步中若修改默认的80端口为8089,则需要关闭服务器防火墙或开放nginx服务端口。
centOS6及以前版本使用命令:
systemctl stop iptables.service
centOS7关闭防火墙命令:
systemctl stop firewalld.service #不过,关闭防火墙会导致服务器有一定风险,所以建议是单独开放服务端口: firewall-cmd --zone=public --add-port=8089/tcp --permanent #查询端口号8089 是否开启: firewall-cmd --query-port=8089/tcp #重启防火墙: firewall-cmd --reload # 查看firewall防火墙状态 systemctl status firewalld # 查看firewall防火墙开放端口 firewall-cmd --list-ports #禁止firewall开机启动 systemctl disable firewalld.service
apt-get --purge autoremove nginx
apt-get update #查看有哪些版本 apt-cache show nginx #安装指定版本的nginx apt-get install nginx=1.18.0-6ubuntu14.4.1
/usr/sbin/nginx:主程序,启动文件
/etc/nginx:存放配置文件
/var/www/html:存放项目目录
/var/log/nginx:存放日志
一般自动安装配置文件目录和主程序目录不变,因为版本原因,其它目录可能会变,但是都可以从配置文件里ngxin.conf里找到对应的位置。
service nginx start service nginx restart service nginx stop
连接MYSQL:
格式: mysql -h主机地址 -u用户名 -p用户密码
1、例1:连接到本机上的MYSQL
找到mysql的安装目录,一般可以直接键入命令mysql -uroot -p,回车后提示你输密码,如果刚安装好MYSQL,超级用户root是没有密码的,故直接回车即可进入到MYSQL中了,MYSQL的提示符是:mysql>
2、连接到远程主机上的MYSQL
假设远程主机的IP为:10.0.0.1,用户名为root,密码为123。则键入以下命令:
mysql -h10.0.0.1 -uroot -p123
(注:u与root可以不用加空格,其它也一样)
3、退出MYSQL命令
exit (回车)
修改密码
方法1: 用SET PASSWORD命令
首先登录MySQL。
格式:mysql> set password for 用户名@localhost = password(‘新密码’);
例子:mysql> set password for root@localhost = password(‘123’);
方法2:用mysqladmin
格式:mysqladmin -u用户名 -p旧密码 password 新密码
例子:mysqladmin -uroot -p123456 password 123
方法3:用UPDATE直接编辑user表
首先登录MySQL。
mysql> use mysql; mysql> update user set password=password(‘123’) where user=‘root’ and host=‘localhost’; mysql> flush privileges;
方法4:在忘记root密码的时候,可以这样
以windows为例:
关闭正在运行的MySQL服务。
打开DOS窗口,转到mysql\bin目录。
输入mysqld --skip-grant-tables 回车。–skip-grant-tables 的意思是启动MySQL服务的时候跳过权限表认证。
再开一个DOS窗口(因为刚才那个DOS窗口已经不能动了),转到mysql\bin目录。
输入mysql回车,如果成功,将出现MySQL提示符 >。
连接权限数据库: use mysql; 。
改密码:update user set password=password(“123”) where user=“root”;(别忘了最后加分号) 。
刷新权限(必须步骤):flush privileges; 。
退出 quit。
注销系统,再进入,使用用户名root和刚才设置的新密码123登录。
数据迁移
原始数据备份命令如下:
ldapsearch -LLL -x -h 218.97.51.117 -p 389 -D "cn=Admin,ou=Administrator,dc=cpccd,dc=com" -w Cpcnet\!\@\#\$\%\^ -b "dc=cpccd,dc=com" > openldap-backupfull.ldif
环境准备:
ubuntu安装docker:
更新ubuntu
sudo apt update sudo apt upgrade sudo apt full-upgrade
添加 Docker 库
sudo apt install apt-transport-https ca-certificates curl software-properties-common gnupg lsb-release
添加 Docker 的官方 GPG 密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
添加 Docker 官方库
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
更新 Ubuntu 源列表
sudo apt update
安装docker
sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin
检查可以安装的docker版本
apt-cache madison docker-ce
安装最新的docker-ce
apt install docker-ce=5:24.0.1-1~ubuntu.22.04~jammy docker-ce-cli=5:24.0.1-1~ubuntu.22.04~jammy containerd.io
启动docker
sudo systemctl start docker
docker服务在每次启动时自动启动
sudo systemctl enable docker
查看docker版本
sudo docker version
docker compose部署openldap
docker-compose.yaml文件路径
/usr/local/docker/openldap2.0/docker-compose.yaml
docker-compose.yaml具体内容如下:
version: "3.7" services: openldap-bitnami: user: root image: bitnami/openldap:2.5.13 container_name: openldap1 volumes: - ./openldap-bitnami-data:/bitnami/openldap environment: - LDAP_ORGANISATION=CPC CD - LDAP_ROOT=dc=cpccd,dc=com - LDAP_ADMIN_USERNAME=admin - LDAP_ADMIN_PASSWORD=Cpcnet!@#$$%^ - LDAP_ULIMIT_MOFILES=107374182400 - LDAP_CONFIG_ADMIN_ENABLED=yes - LDAP_CONFIG_ADMIN_USERNAME=admin - LDAP_CONFIG_ADMIN_PASSWORD=admin - LDAP_ALLOW_ANON_BINDING=no - LDAP_USER_DC=people ports: - 1389:1389 - 1636:1636 networks: - openldap-bitnami networks: openldap-bitnami: name: openldap-bitnami
运行命令:
Docker-compose up –d
验证ldap容器是否创建成功命令如下:
ldapsearch -x -H ldap://localhost:389 -b 'dc=cpccd,dc=com' -D 'cn=admin,dc=cpccd,dc=com' -w 'Cpcnet!@#$%^'
为每一个openldap服务添加同步用的用户
需要使用管理账号登录,分别在所有节点导入以下配置。
syncrepl_user.ldif
version: 1 dn: uid=rpuser,ou=people,dc=demo,dc=com objectClass: top objectClass: person objectClass: organizationalPerson objectClass: simpleSecurityObject objectClass: shadowAccount objectClass: inetOrgPerson cn: rpuser sn: rpuser uid: rpuser userPassword: {SSHA}oBWZtlM7e3CbUagQeUPqkVeFUmg2+4liXvRfVQ==
用户dn:rpuser,ou=people,dc=demo,dc=com"
密码:rpuser
ldapmodify -a -x -H ldap://localhost:389 -D 'cn=admin,dc=cpccd,dc=com' -w 'Cpcnet!@#$%^' -f syncrepl_user.ldif
各节点同步文件
需要使用配置管理账号登录,在所有同步节点导入以下配置。
syncrepl_config.ldif
#enable syncprov module dn: cn=module,cn=config objectClass: olcModuleList cn: module olcModulePath: /opt/bitnami/openldap/lib/openldap olcModuleLoad: syncprov.so #enable syncprov for every folder dn: olcOverlay=syncprov,olcDatabase={2}mdb,cn=config changetype: add objectClass: olcOverlayConfig objectClass: olcSyncProvConfig olcOverlay: syncprov olcSpSessionlog: 100 #unlimit fetch size dn: cn=config changetype: modify replace: olcSizeLimit olcSizeLimit: 10000 #set server id dn: cn=config changetype: modify replace: olcServerID olcServerID: 1 #syncrepl directive dn: olcDatabase={2}mdb,cn=config changetype: modify replace: olcSyncRepl olcSyncRepl: rid=001 provider=ldap://需要同步的ip:389/ bindmethod=simple binddn="uid=rpuser,ou=people,dc=cpccd,dc=com" credentials=rpuser searchbase="dc=cpccd,dc=com" scope=sub schemachecking=on type=refreshAndPersist retry="5 +" interval=00:00:30:00 - add: olcMirrorMode olcMirrorMode: TRUE olcSyncRepl: rid=002 provider=ldap://需要同步的ip:389/ bindmethod=simple binddn="uid=rpuser,ou=people,dc=cpccd,dc=com" credentials=rpuser searchbase="dc=cpccd,dc=com" scope=sub schemachecking=on type=refreshAndPersist retry="5 +" interval=00:00:30:00 - add: olcMirrorMode olcMirrorMode: TRUE olcSyncRepl: rid=003 provider=ldap://需要同步的ip:389/ bindmethod=simple binddn="uid=rpuser,ou=people,dc=cpccd,dc=com" credentials=rpuser searchbase="dc=cpccd,dc=com" scope=sub schemachecking=on type=refreshAndPersist retry="5 +" interval=00:00:30:00 - add: olcMirrorMode olcMirrorMode: TRUE
该脚本设置了搜索的大小限制为10000,从dc=cpccd,dc=com搜索并同步数据,每30秒同步一次,失败会在5秒后重试。请根据实际的环境和项目要求进行修改。
ldapadd -Wx -D "cn=admin,cn=config" -H ldap://localhost:389 -f syncrepl_config.ldif
修改用户权限
需要使用配置管理账号登录,在所有节点分别导入以下配置。
db_config.ldif
dn: cn=config changetype: modify replace: olcSizeLimit olcSizeLimit: 10000 #db max size dn: olcDatabase={2}mdb,cn=config changeType: modify add: olcDbMaxSize olcDbMaxSize: 1000000000
执行以下命令:
ldapmodify -Wx -D "cn=admin,cn=config" -H ldap://localhost:389 -f db_config.ldif
选择任意一台ldap服务器进行数据导入:
ldapmodify -a -x -H ldap://localhost:389 -D 'cn=admin,dc=cpccd,dc=com' -w Cpcnet\!\@\#\$\%\^ -f openldap-backupfull.ldif -v -c
查看导入结果:
ldapsearch -x -H ldap://localhost:389 -b 'dc=cpccd,dc=com' -D 'cn=admin,ou=Administrator,dc=cpccd,dc=com' -w 'Cpcnet!@#$%^' 'mail=demo01@citictel-cpc.com'
前言
在开发中经常需要将json字符串转换为java对象或者list数组,这样方便我们来使用这些数据,下面就来介绍一下怎样将json字符串和json数组转换为Java对象或者list数组。
本次使用阿里巴巴的fastjson来解析json字符串,需要添加依赖:
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.8</version> </dependency>
一、什么是JSON对象?
在大括号 {…} 中书写,包含多个 key/value(键/值)对,key 必须是字符串,value 可以是合法的 JSON 数据类型(字符串, 数字, 对象, 数组, 布尔值或 null) 例如一个最简单的没有嵌套的JSON对象:
{"name":"Sun", "age":18, "site":null}
二、什么是json字符串?
书写格式和JSON对象一样,不过类型属于String,不能直接操作其中的各个属性,需要对其进行转换。在很多前后端交互传值之类的,都以json字符串的形式进行传递。,所以对json字符串进行转换非常有必要。
二、什么是JSON数组?
JSON 数组在中括号中书写,JSON 中数组值必须是合法的 JSON 数据类型(字符串, 数字, 对象, 数组, 布尔值或 null)。 简单的JSON数组:
["Google", "Runoob", "Taobao"]
三、复杂、嵌套的json字符串
复杂的是json字符串里面嵌套json数组,这也是转换的重点,例如:
{ "student": [ { "name": "Tom", "Grade": 1, "age": 11, "gender": "M" }, { "name": "Jerry", "Grade": 1, "age": 10, "gender": "M" } ], "classroom": { "class1": "room1", "class2": "room2" } }
在这里推荐几个比较好用的json字符串校验和格式化的网站:
四、json字符串转换
4.1 简单json字符串转换为java对象
转换语句:
//方法1 将json字符串转换为java对象 Student student =JSON.parseObject(jsonStr,Student .class); //方法2 先将json字符串转换成JSONObject对象 JSONObject jsonObject1 = new JSONObject(JSON.parseObject(jsonStr)); //将转换后的JSONObject对象转换成Student对象 Student student1 = (Student) JSONObject.toJavaObject(jsonObject1,Student.class); 示例:解析如下json字符串 { “name”: “Tom”, “Grade”: 1, “age”: 11, “gender”: “M” } 首先创建一个对应的对象: 1 public class Student { private String name; private int grade; private int age; private String gender; //省略get、set方法 } public void testJson(){ String jsonStr = "{\n" + " \"name\": \"Tom\", \n" + " \"Grade\": 1, \n" + " \"age\": 11, \n" + " \"gender\": \"M\"\n" + " }"; JSONObject jsonObject = new JSONObject(JSON.parseObject(jsonStr)); System.out.println("jsonObject:"+jsonObject); System.out.println("name:"+jsonObject.get("name")); System.out.println("Grade:"+jsonObject.get("Grade")); System.out.println("age:"+jsonObject.get("age")); //方法1 将json字符串转换为java对象 Student student =JSON.parseObject(jsonStr,Student .class); System.out.println("student:"+student); //方法2 先将json字符串转换成JSONObject对象 JSONObject jsonObject1 = new JSONObject(JSON.parseObject(jsonStr)); //将转换后的JSONObject对象转换成Student对象 Student student1 = (Student) JSONObject.toJavaObject(jsonObject1,Student.class); System.out.println("student1:"+student1); //转换为java对象之后可以利用对象的get方法来取值 System.out.println("age:"+student.getAge()); }
4.2 简单json字符串数组转换为list数组
使用语句:
List<Student> studentList = JSONObject.parseArray(jsonStr, Student.class); 示例: [ { “name”: “Tom”, “Grade”: 1, “age”: 11, “gender”: “M” }, { “name”: “Jerry”, “Grade”: 1, “age”: 10, “gender”: “M” } ] 首先创建一个对应的java对象: public class Student { private String name; private int grade; private int age; private String gender; //省略get、set方法 } public void testJson(){ String jsonStr = "[\n" + " {\n" + " \"name\": \"Tom\", \n" + " \"Grade\": 1, \n" + " \"age\": 11, \n" + " \"gender\": \"M\"\n" + " }, \n" + " {\n" + " \"name\": \"Jerry\", \n" + " \"Grade\": 1, \n" + " \"age\": 10, \n" + " \"gender\": \"M\"\n" + " }\n" + "]"; List<Student> studentList = JSONObject.parseArray(jsonStr, Student.class); System.out.println("studentList:"+studentList); for (int i = 0; i < studentList.size(); i++) { System.out.println("name:"+studentList.get(i).getName()); System.out.println("Grade:"+studentList.get(i).getGender()); System.out.println("age:"+studentList.get(i).getAge()); } }
4.3 复杂嵌套json字符串数组转换为Java对象、list数组
使用语句:
示例:
{ “student”: [ { “name”: “Tom”, “Grade”: 1, “age”: 11, “gender”: “M” }, { “name”: “Jerry”, “Grade”: 1, “age”: 10, “gender”: “M” } ], “classroom”: { “class1”: “fifth floor”, “class2”: “seventh floor” } } 首先创建对应的java对象,上面的Student对象可以重复使用,这里需要再添加两个对象,一个整体的包含Student和Classroom的对象School ,一个Classroom对象: public class School { //这里的Student和Classroom声明为Object类型 private Object Student; private Object Classroom; //省略get、set方法 } public class Classroom { private String class1; private String class2; //省略get、set方法 } 进行转换: public void testJson(){ String jsonStr = "{\n" + " \"student\": [\n" + " {\n" + " \"name\": \"Tom\", \n" + " \"Grade\": 1, \n" + " \"age\": 11, \n" + " \"gender\": \"M\"\n" + " }, \n" + " {\n" + " \"name\": \"Jerry\", \n" + " \"Grade\": 1, \n" + " \"age\": 10, \n" + " \"gender\": \"M\"\n" + " }\n" + " ], \n" + " \"classroom\": {\n" + " \"class1\": \"fifth floor\", \n" + " \"class2\": \"seventh floor\"\n" + " }\n" + "}"; //方法1 调用JSON.parseObject方法将json字符串转换为java对象 School school =JSON.parseObject(jsonStr,School.class); System.out.println("school:"+school); //方法2 先将json字符串转换成JSONObject对象 JSONObject jsonObject = new JSONObject(JSON.parseObject(jsonStr)); //将转换后的JSONObject对象整体转换成School对象 School school1 = (School) JSONObject.toJavaObject(jsonObject,School.class); System.out.println("school1:"+school1); //将School中的student数组转换成list对象 List<Student> studentList = JSONObject.parseArray(school.getStudent().toString(), Student.class); System.out.println("studentList:"+studentList); for (int i = 0; i < studentList.size(); i++) { System.out.print("name:"+studentList.get(i).getName()+","); System.out.print("Grade:"+studentList.get(i).getGender()+","); System.out.println("age:"+studentList.get(i).getAge()); } //将School中的classroom转换成java对象 与刚开始转换school的思路是一样的 //从school对象中getClassroom,返回的是一个Object对象类型 Object classroomObj = school.getClassroom(); //用toString()方法将Object对象转换成String String strClassroom = school.getClassroom().toString(); //方法1 调用JSON.parseObject方法将json字符串转换为java对象 Classroom classroom1 =JSON.parseObject(strClassroom,Classroom.class); System.out.println("classroom1:"+classroom1); //方法2 先将json字符串转换成JSONObject对象 JSONObject jsonClassroom = new JSONObject(JSON.parseObject(strClassroom)); //再利用JSONObject.toJavaObject转换为java对象 Classroom classroom2 = (Classroom) JSONObject.toJavaObject(jsonClassroom,Classroom.class); System.out.println("classroom2:"+classroom2); }
4.3.2 示例2
使用语句:
在4.3.1的基础上新增了两个字段:schoolName和address。Student对象和Classroom对象可以使用,需要重新创建一个整体的对象HighSchool
示例:
{ “schoolName”: “ECNU”, “address”: “Road”, “student”: [{ “name”: “Tom”, “Grade”: 1, “age”: 11, “gender”: “M” }, { “name”: “Jerry”, “Grade”: 1, “age”: 10, “gender”: “M” } ], “classroom”: { “class1”: “fifth floor”, “class2”: “seventh floor” } } 首先创建对应的java对象,上面的Student对象可以重复使用,这里需要再添加两个对象,一个整体的包含Student和Classroom的对象,一个Classroom对象: public class School { //这里的Student和Classroom声明为Object类型 private Object Student; private Object Classroom; //省略get、set方法 } public class Classroom { private String class1; private String class2; //省略get、set方法 } 进行转换: public void testJson(){ String jsonStr = "{\n" + " \"schoolName\": \"ECNU\", \n" + " \"address\": \"Road\", \n" + " \"student\": [\n" + " {\n" + " \"name\": \"Tom\", \n" + " \"Grade\": 1, \n" + " \"age\": 11, \n" + " \"gender\": \"M\"\n" + " }, \n" + " {\n" + " \"name\": \"Jerry\", \n" + " \"Grade\": 1, \n" + " \"age\": 10, \n" + " \"gender\": \"M\"\n" + " }\n" + " ], \n" + " \"classroom\": {\n" + " \"class1\": \"fifth floor\", \n" + " \"class2\": \"seventh floor\"\n" + " }\n" + "}"; //方法1 调用JSON.parseObject方法将json字符串转换为java对象 HighSchool highSchool = JSON.parseObject(jsonStr,HighSchool.class); System.out.println("highSchool:"+ highSchool); System.out.println("schoolName:"+ highSchool.getSchoolName()); System.out.println("address:"+ highSchool.getAddress()); //方法2 与之前讲过的一样,这里就省略掉了 //将School中的student数组转换成list对象 List<Student> studentList = JSONObject.parseArray(highSchool.getStudent().toString(), Student.class); System.out.println("studentList:"+studentList); for (int i = 0; i < studentList.size(); i++) { System.out.print("name:"+studentList.get(i).getName()+","); System.out.print("Grade:"+studentList.get(i).getGender()+","); System.out.println("age:"+studentList.get(i).getAge()); } //HighSchool 与刚开始转换school的思路是一样的 //从highSchool对象中getClassroom,返回的是一个Object对象类型 Object classroomObj = highSchool.getClassroom(); //用toString()方法将Object对象转换成String String strClassroom = highSchool.getClassroom().toString(); //方法1 调用JSON.parseObject方法将json字符串转换为java对象 Classroom classroom1 =JSON.parseObject(strClassroom,Classroom.class); System.out.println("classroom1:"+classroom1); //方法2 与之前讲过的一样,这里就省略掉了 }
总结
转换大体上分为两种:
1、以{……}大括号包裹的数据要转换为java对象;
2、 以[ ] 中括号包裹的数据转换为list数组。
首先要明白,以 { } 开头的是JSONObject,以 [ ] 开头的是JSONArray,如果本身就是json字符串的格式(即格式类型为String),那就不用转换,直接使用转换方法进行转换。
但是如果是嵌套字符串的话,从第一次转换后的java对象中取出来的是Object类型,这时候需要先用toString方法转换为String类型,然后再调用方法。