Cordova移动应用对云端服务器数据库的跨域访问
当基于类似 Cordova这样的跨平台开发框架进行移动应用的跨平台开发时,往往需要访问部署在公网云端服务器上的数据库,这时就涉及到了跨域数据访问的问题。
一、跨域访问的概念
跨域(CORS, Cross-Origin Resource Sharing)指的是一个源(origin)的网页或应用,访问另一个源上的资源或接口时,就发生了“跨域”。跨域概念的起源是由于同源策略(Same-origin_policy),该策略是1995年由 Netscape 公司引入浏览器的最核心也是最基本的安全策略。同源指域名(IP)、协议、端口需要相同才能完成彼此资源的访问和读写。而不同源的客户端脚本在没有明确授权的情况下,不允许读写对方的资源,浏览器会报异常:“拒绝访问”。主要目的是避免在客户不知情的情况下出现安全问题。
因此,跨域只要存在不同IP地址、不同域名(包括子域名)、不同端口地址,都属于“跨域”访问。例如:
跨域访问 | 原因 |
---|---|
http://www.a.com/index.html 调用 http://www.b.com/server.php | 主域名不同 |
http://www.a.com/index.html 调用 http://www.b.com/server.php | 子域名不同 |
http://www.a.com/index.html 调用 http://www.b.com/server.php | 子域名不同 |
http://www.a.com:80/index.html 调用 http://www.a.com:8080/index.html | 端口不同 |
http://www.a.com/index.html 调用 https://www.a.com/server.php | 协议不同 |
然而,从上面实例可见,跨域访问的需求在实际应用中普遍存在,特别是在当今互联网已成为基础设施的情况下,例如:
1、集团公司或组织下,多个子域间的资源共享和访问
2、客户端和服务端之间的远程资源交互和访问
3、跨平台开发需要将数据集中在公网服务器,而移动客户端通过远程完成数据的共享和并发。
在上述场景中,都涉及到跨不同域名、IP或端口的访问。
二、跨域访问在Cordova中的实现实例
1、运行环境
本实例的运行开发环境基于如下架构:
(1) 公网WWW服务器
IP地址假设为:10.10.10.111
www服务:apache
后端开发语言:php
数据库:mysql(mariaDB)
(2) 前端开发工具
javascript+Html+css
cordova
2、实例
本实例实现基于cordova 编译运行的跨平台移动app,访问公网10.10.10.1中安装的数据库中的users表,当用户输入username,userpassword后,选择“新用户注册”,则向数据库表中新增一条用户,未选中,则查询表中是否存在uname和upass分别等于用户输入的username和userpassword的记录。
users表结构如下:
生成库的SQL语句:
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uname` char(30) NOT NULL,
`upass` varchar(30) DEFAULT NULL,
`utype` tinyint(1) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;
(1)前端代码
cordova工程中index.html内容如下:
其中使用了jqueryMobile,下载后需要放到www目录下的lib目录
<!--This is for mobile -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Web SQLite Test</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="lib/jquery.mobile-1.4.5.min.css" />
<script src="lib/jquery-2.1.1.min.js"></script>
<script src="lib/jquery.mobile-1.4.5.min.js"></script>
<!--<script type="text/javascript" charset="utf-8" src="cordova.js">-->
</script>
<script type="text/javascript">
$(document).ready(function() {
$("#submitbtn").bind("click", function () {
$.ajax({
type: "post",
url: "http://10.10.10.111/myUserlogin_Server/valid.php",//公网云服务器地址
data: {username:$('#username').val(),
password:$('#userpassword').val(),
registerit:$("#newuser").prop("checked"),
},
datatype: "jsonp",
success : onSuccess,
error : onError
});
return false;
});
});
function onSuccess(data,status){
data = $.trim(data); //去掉前后空格
alert(data);
}
function onError(data,status){
//进行错误处理
console.info("网络出错");
}
</script>
</head>
<body>
<div data-role="page">
<div data-role="header"><h1>系统登录</h1></div>
<div data-role="content" class="ui-content">
<form method="post" id="feedbackform">
<div class="ui-field-contain">
<label for="username">用户名:</label>
<input type="text" name="username" id="username" placeholder="请输入用户名">
<label for="userpassword">密   码:</label>
<input type="password" name="userpassword" id="userpassword" placeholder="请输入密码">
<label for="newuser">新用户注册</label>
<input type="checkbox" name="newuser" id="newuser" value="1">
</div>
<input type="button" id="submitbtn" class="ui-btn ui-btn-a" value="提交">
</form>
<button id="ModifyRec" >取消</button>
<div id="database_results"></div>
</div>
</body>
</html>
(2)后端代码
在服务器中,安装了apache+php的解释器,并在htdocs中建立文件夹myUserlogin_Server,里面的valid.php用于根据前端提交的数据完成数据库的相应操作。
<?php
header("Access-Control-Allow-Origin:*");//允许所有域名的脚本跨域访问该资源,从安全角度建议设置更严格的权限
$user=$_POST["username"];
$pass=$_POST["password"];
$newuser=$_POST["registerit"];
/*echo '用户名:'.$user;
echo '密码:'.$pass;
echo '是否需注册:'.$newuser;*/
// PDO方式连接
try {
$conn = new PDO("mysql:host=localhost;dbname=cordovatestdb", '登录用户名', '登录密码');
// echo "连接mysql的test数据库成功";
}
catch (PDOException $e) {
die('连接失败:' . $e->getMessage());
}
$conn->exec("set names 'utf8'");
if($newuser=='true') //注册新用户
{
$sql = "SELECT id, uname, utype FROM users where uname=?";
if (QueryDB($conn,$sql,array($user))!=null){
//已有同名记录
echo "已经注册过,直接登录!";
}
else { //进行注册,新增该用户
$sql = "INSERT INTO users (uname, upass, utype)
VALUES (?, ?, 1)";
$stmt = $conn->prepare($sql);
$rs = $stmt->execute(array($user,$pass));
if ($rs) {
echo "按输入的用户名和密码,注册成功!";
$sql = "SELECT id, uname, utype FROM users where uname=? and upass=?";
$rows = QueryDB($conn,$sql,array($user,$pass));
if ($rows!=null){//验证注册记录是否保存成功
print_r($rows); //输出记录
}
else {
echo "注册失败: " . $sql . "<br>" . $conn->error;
}
}
}
}
else{ //登录
$sql = "SELECT id, uname, utype FROM users where uname=? and upass=?";
$rows = QueryDB($conn,$sql,array($user,$pass));
if ($rows!=null){//登录验证记录存在
// 验证用户成功,输出数据
echo "登录成功!";
print_r($rows);
}
else {
echo "用户名或者密码错误!";
}
}
$conn = null;
Function QueryDB($dbo,$sql,$paraStr)
{
$stmt = $dbo->prepare($sql);
$rs = $stmt->execute($paraStr);
$rows = $stmt->fetchAll();
$row_count = $stmt->rowCount(); //记录数
if ($rs && $row_count>0)
return $rows;
else {
return 0;
}
}
?>
注意:前端代码也可以直接采用javascript的fetch语句完成http请求。
参考代码如下:
const apiURL = "https://10.10.10.111/valid.php"; // 你的PHP地址
function login() {
const username = document.getElementById("username").value;
const userpassword = document.getElementById("userpassword").value;
const registerit = document..getElementById("newuser").checked;
fetch(apiURL, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
username: username,
password: userpassword,
registerit: registerit
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert("登录成功!");
// 继续处理登录后的逻辑
} else {
alert("登录失败:" + data.message);
}
})
.catch(error => {
alert("请求出错:" + error);
});
}