java实现后端身份验证的Token令牌

java实现后端身份验证的Token令牌

写在前面的话:
蒽,Token是一个什么东西?那我们为什么要用它?

Token:Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端的请求只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。

使用Token的目的:Token的目的是为了减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮。

同时在分布式中我们会选用Token来身份验证,而不是session。 原因就是:
session是有状态的,一般存于服务器内存中,当服务器采用分布式时,session就会面对负载均衡问题如果遇到负载均衡多服务器的情况,那我们就不好确认当前用户是否登录,因为多服务器不共享session。但是token是无状态的,token字符串里就保存了所有的用户关键信息

看一个简单的Token结构:
在这里插入图片描述
简单说一下,Token的原理:

1、上面图片中的Token,前面一串"id:1001,status:1,endtime:2022-01-08 19:18:43"字符串就是服务端在用户第一次登录的时候前端传入的用户名及密码验证成功,后端就查询该用户的id以及状态,即该账号是否正常使用中,或是已经停用的账号,endtime是后端产生Token的当前时间加一个有效时间,作为验证Token的有效时间。后面的fb675266364f697519dac6d1e6ec1da3 是根据前面的字符串生成的MD5加密码。
2、ImlkOjEwMDIzLHN0YXR1czowLGVuZHRpbWU6MjAyMi0wMS0wOSAyMDozNTo0OCI7MjEwOTJkNDRkNzVjOTM2NzY0YThiODA3ZmEzYWU1NzE=
这一串是后端返回给前端看到的数据,当然就是把"id:1001,status:1,endtime:2022-01-08 19:18:43";fb675266364f697519dac6d1e6ec1da3进行base64编码,对数据进行一定保护。
3、前端收到后端的登录成功消息后,同时保存Token,以后的每次请求都要带上Token(放在请求头)。
4、后端接收到前端的业务请求后,首先验证Token是否有效,即Token中的数据有没有更改(切确的说是看有没有人伪造用户来后端请求,冒充用户)。
5、这里各位看友可能会有点迷糊,请看下面简单的交互图

在这里插入图片描述
现在开始代码阶段:

1、先数据库连接成功
2、接受前端请求,后端查询数据库判断是否创建Token,即该用户是否合法
3、合法验证成功,即开始创建Token并传给前端
4、前端拿到Token并保存,当前登录成功
5、前端再次发送请求给后端,带上Token(放在请求头内)
6、后端接受请求,首先验证Token是否有效,若有效则返回前端的业务请求,并将当前新的Token传给前端
7、前端保存新的Token,为后面的请求,即完成一个Token的流程

1、数据库的连接如下:

 private static final java.lang.String url = "jdbc:mysql://localhost:3306/xiaofang?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";private static final java.lang.String userName = "root";private static final java.lang.String passwd = "root";static {try {Class.forName("com.mysql.cj.jdbc.Driver");} catch (ClassNotFoundException e) {e.printStackTrace();}}**数据库文件:**```sql
/*Navicat Premium Data TransferSource Server         : mysqlSource Server Type    : MySQLSource Server Version : 80025Source Host           : localhost:3306Source Schema         : xiaofangTarget Server Type    : MySQLTarget Server Version : 80025File Encoding         : 65001Date: 09/01/2022 22:23:32
*/SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (`id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`phone` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`head` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`addr` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`status` int(0) NULL DEFAULT NULL,`created` datetime(6) NULL DEFAULT NULL,`deleted` datetime(6) NULL DEFAULT NULL,`type` int(0) NOT NULL,PRIMARY KEY (`username`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1001', 'cc', 'cc', 'cc123', NULL, '/static/img/user.jpg', NULL, 1, '2022-01-08 19:00:00.000000', NULL, 0);
INSERT INTO `user` VALUES ('10023', 'tt', 'tt', 'tt456', NULL, NULL, NULL, NULL, NULL, NULL, 0);SET FOREIGN_KEY_CHECKS = 1;

2、同样的前端我们不写页面,后端直接用Servlet的doget拿参数,来验证:
不清楚Servlet前后端交互的看友,可以看看我前面的这篇文章Servlet前后端简单交互

 	@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setHeader("Content-Type", "application/json;charset=UTF-8");String name = req.getParameter("name");String password = req.getParameter("password");try (Connection conn = DriverManager.getConnection(url, userName, passwd);PreparedStatement ps = conn.prepareStatement("select * from user where name =? and password =?;")) {ps.setString(1, name);ps.setString(2, password);ResultSet rs = ps.executeQuery();//字符串等于比较,要记得不要混了用equalswhile (rs.next()) {if ((rs.getString("name").equals(name)) && (rs.getString("password").equals(password))) {resp.getWriter().println("登录成功!欢迎您" + rs.getString("name"));int id = rs.getInt("id");int status = rs.getInt("status");resp.getWriter().println("Token的值为:" + createToken(id, status));} else {resp.getWriter().println("-----登录失败!-----");resp.getWriter().println("-----用户名或密码错误!-----");}}rs.close();} catch (SQLException throwables) {throwables.printStackTrace();}}

3、合法验证成功,即开始创建Token并传给前端

/*** 创建Token对象** @param id* @param status*/public static String createToken(int id, int status) throws UnsupportedEncodingException {SimpleDateFormat sif = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date date = new Date();//当前时间加五分钟,作为Token的有效时间Date dateafter = new Date(date.getTime() + 300000);Object endtime = sif.format(dateafter);String json = "id:" + id + ",status:" + status + ",endtime:" + endtime;JSONObject jsonObject = new JSONObject();String ss = jsonObject.toJSONString(json);String begin = ss + ";" + getMd5(ss);//调用并转换成base64//这里的Base64码转换idea提供了相关的方法,我是调用了自己写的Base64码,想看的可以关注我,看我的文章哦return Base644.Encoding(begin);}

将Token传给前端的代码,写在了登录验证成功就创建并传递给前端

  resp.getWriter().println("Token的值为:" + createToken(id, status));

在这里插入图片描述
4、前端再次发送请求给后端,带上Token(放在请求头内)
这里有多种请求方法,比如Postman,或者Idea的.http请求文件
在这里插入图片描述
在这里插入图片描述
Token.http内容:
要知道这个是前端的请求,要带上我们前面后端给的Token

POST http://localhost:8080/war/helloworld
Content-Type: application/json;charset=utf-8
Token: ImlkOjEwMDEsc3RhdHVzOjEsZW5kdGltZToyMDIyLTAxLTA5IDIwOjE4OjAwIjszZjRmYzMyMWE3Y2FlNThmMjhmNzBkYmYxNTg2YmNkMg=={"id": 1001,"name": "云先生","date": [{"create_time": "2022-01-06"}]
}

在这里插入图片描述
5、后端接受请求,首先验证Token是否有效,若有效则返回前端的业务请求,并将当前新的Token传给前端。
在这里插入图片描述

@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setHeader("Content-Type", "application/json;charset=UTF-8");try {timethan(req, resp);//如果用户身份的token验证通过,进行前端请求的业务操作,// 并返回前端一个新的token} catch (ParseException e) {e.printStackTrace();}}/*** 后端拿到前端请求头的token对象,验证身份,比较有效时间** @param req* @param resp* @return* @throws ParseException*/public static void timethan(HttpServletRequest req, HttpServletResponse resp) throws ParseException, IOException {//拿到token对象String str = Base644.Decoding(req.getHeader("Token"));//将token切割成两部分//str:"id:1001,status:1,endtime:2022-01-08 19:18:43";fb675266364f697519dac6d1e6ec1da3String ahead = str.substring(0, str.indexOf(";"));String behind = str.substring(str.indexOf(";") + 1, str.length());//时间从字符串中截取出来String ss = ahead.substring(ahead.indexOf("e"), ahead.length());String cc = ss.substring(ss.indexOf(":") + 1, ss.length());//将字符串的时间转为long类型的数据进行比较SimpleDateFormat sif = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date data = sif.parse(cc);long stime = data.getTime();Date date1 = new Date();Date dateafter = new Date(date1.getTime());long nowtime = dateafter.getTime();long differen = stime - nowtime;//解析当前token对象的id,状态int id = Integer.parseInt(ahead.substring(ahead.indexOf("i") + 3, ahead.indexOf(",")));int status = Integer.parseInt(ahead.substring(ahead.indexOf("s") + 7, ahead.lastIndexOf(",")));if (getMd5(ahead).equals(behind)) {try {if (differen < 0) {resp.getWriter().println("Token有效时间已过,请重新登录!");} else {resp.getWriter().println("前端请求的业务处理结果,后端已送到,请接受!");resp.getWriter().println("Token:"+createToken(id,status));}} catch (IOException e) {e.printStackTrace();}} else {resp.getWriter().println("Token信息有误!");}}

最后,前端继续把Token保存,留作下一次的使用。 到了这里Token一个的前后端交互就完成了。

全部源码:

package com.example.demo.newyears;import com.alibaba.fastjson.JSONObject;import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.*;// 先数据库连接成功
// 接受前端请求,后端查询数据库判断是否创建Token,即该用户是否合法
// 合法验证成功,即开始创建Token并传给前端
// 前端拿到Token并保存,当前登录成功
// 前端再次发送请求给后端,带上Token(放在请求头内)
// 后端接受请求,首先验证Token是否有效,若有效则返回前端的业务请求,并将当前新的Token传给前端
// 前端保存新的Token,为后面的请求,即完成一个Token的流程/*** @Author:Yun* @Date:2022/01/08/11:50* @Description:**/
@WebServlet(name = "test", urlPatterns = "/helloworld")
public class Token extends HttpServlet {private static final java.lang.String url = "jdbc:mysql://localhost:3306/xiaofang?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";private static final java.lang.String userName = "root";private static final java.lang.String passwd = "root";static {try {Class.forName("com.mysql.cj.jdbc.Driver");} catch (ClassNotFoundException e) {e.printStackTrace();}}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setHeader("Content-Type", "application/json;charset=UTF-8");String name = req.getParameter("name");String password = req.getParameter("password");try (Connection conn = DriverManager.getConnection(url, userName, passwd);PreparedStatement ps = conn.prepareStatement("select * from user where name =? and password =?;")) {ps.setString(1, name);ps.setString(2, password);ResultSet rs = ps.executeQuery();//字符串等于比较,要记得不要混了用equalswhile (rs.next()) {if ((rs.getString("name").equals(name)) && (rs.getString("password").equals(password))) {resp.getWriter().println("登录成功!欢迎您" + rs.getString("name"));int id = rs.getInt("id");int status = rs.getInt("status");resp.getWriter().println("Token的值为:" + createToken(id, status));} else {resp.getWriter().println("-----登录失败!-----");resp.getWriter().println("-----用户名或密码错误!-----");}}rs.close();} catch (SQLException throwables) {throwables.printStackTrace();}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setHeader("Content-Type", "application/json;charset=UTF-8");try {timethan(req, resp);//如果用户身份的token验证通过,进行前端请求的业务操作,// 并返回前端一个新的token} catch (ParseException e) {e.printStackTrace();}}/*** 后端拿到前端请求头的token对象,验证身份,比较有效时间** @param req* @param resp* @return* @throws ParseException*/public static void timethan(HttpServletRequest req, HttpServletResponse resp) throws ParseException, IOException {//拿到token对象String str = Base644.Decoding(req.getHeader("Token"));//将token切割成两部分//str:"id:1001,status:1,endtime:2022-01-08 19:18:43";fb675266364f697519dac6d1e6ec1da3String ahead = str.substring(0, str.indexOf(";"));String behind = str.substring(str.indexOf(";") + 1, str.length());//时间从字符串中截取出来String ss = ahead.substring(ahead.indexOf("e"), ahead.length());String cc = ss.substring(ss.indexOf(":") + 1, ss.length());//将字符串的时间转为long类型的数据进行比较SimpleDateFormat sif = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date data = sif.parse(cc);long stime = data.getTime();Date date1 = new Date();Date dateafter = new Date(date1.getTime());long nowtime = dateafter.getTime();long differen = stime - nowtime;//解析当前token对象的id,状态int id = Integer.parseInt(ahead.substring(ahead.indexOf("i") + 3, ahead.indexOf(",")));int status = Integer.parseInt(ahead.substring(ahead.indexOf("s") + 7, ahead.lastIndexOf(",")));if (getMd5(ahead).equals(behind)) {try {if (differen < 0) {resp.getWriter().println("Token有效时间已过,请重新登录!");} else {resp.getWriter().println("前端请求的业务处理结果,后端已送到,请接受!");//创建一个新的Tokenresp.getWriter().println("Token:"+createToken(id,status));}} catch (IOException e) {e.printStackTrace();}} else {resp.getWriter().println("Token信息有误!");}}/*** 创建Token对象** @param id* @param status*/public static String createToken(int id, int status) throws UnsupportedEncodingException {SimpleDateFormat sif = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date date = new Date();Date dateafter = new Date(date.getTime() + 300000);Object endtime = sif.format(dateafter);String json = "id:" + id + ",status:" + status + ",endtime:" + endtime;JSONObject jsonObject = new JSONObject();String ss = jsonObject.toJSONString(json);System.out.println(ss);String begin = ss + ";" + getMd5(ss);//调用并转换成base64return Base644.Encoding(begin);}/*** 将字符串转为MD5加密的方法** @param org* @return*/public static String getMd5(String org) {MessageDigest md = null;try {md = MessageDigest.getInstance("MD5");md.reset();md.update(org.getBytes(StandardCharsets.UTF_8));byte[] byteArray = md.digest();StringBuilder md5StrBuff = new StringBuilder();for (int i = 0; i < byteArray.length; i++) {if (Integer.toHexString(0xFF & byteArray[i]).length() == 1)md5StrBuff.append("0").append(Integer.toHexString(0xFF & byteArray[i]));elsemd5StrBuff.append(Integer.toHexString(0xFF & byteArray[i]));}return md5StrBuff.toString();} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return "";}
}

最后有问题的看友,可以留言或者私聊我欧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/60714.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TOKEN身份验证令牌

目录 1、作用 2、使用场景 3、token原理 4、示例 1、作用 一般用来验证用户是否还在有效登录时间&#xff0c;属于服务器端技术&#xff0c;保存在服务器中 2、使用场景 很多网站会保存一定时间的用户登录状态&#xff08;解决登录安全问题&#xff0c;一般不会永久保存&a…

用户身份验证的令牌—Token教程

一、什么是Token&#xff1f; 1、Token的引入&#xff1a;Token是在客户端频繁向服务端请求数据&#xff0c;服务端频繁的去数据库查询用户名和密码并进行对比&#xff0c;判断用户名和密码正确与否&#xff0c;并作出相应提示&#xff0c;在这样的背景下&#xff0c;Token便应…

【加餐 2】Tab 标签页管理

【加餐 2】Tab 标签页管理 对于管理系统,经常需要开启多个标签页,但是每次都需要手动去关闭,很麻烦,所以就有了这个功能,可以一键关闭所有标签页,或者关闭除当前标签页外的所有标签页,对于重要的标签页,可以进行固定至前列,方便下次快速打开。 一、实现效果 实现效…

从零开始的ChatGLM 配置详细教程

从零开始的ChatGLM配置教程 文章目录 从零开始的ChatGLM配置教程一&#xff0c;前言二&#xff0c;环境配置1、下载ChatGLM项目2、配置程序运行环境 三、在HuggingFace下载chatGLM-6B模型1&#xff0c;安装 Git Lfs2&#xff0c;下载相关文件3&#xff0c;在HuggingFace中下载相…

蜜雪冰城、茶颜悦色“卷”向咖啡赛道

配图来自Canva可画 一年一度的“双11”电商节战报&#xff0c;揭示了各行各业的品牌热度&#xff0c;今年多个咖啡品牌赫然登上爆火品牌的榜单。据了解&#xff0c;新锐品牌三顿半再次登顶速溶咖啡类目TOP1&#xff0c;截止到11月11日0点45分&#xff0c;今年双11累计成交额突…

ロゼッタ / 火奶

目录 基本资料面板值&#xff08;无天冥加成&#xff09;天冥奖励 战斗宣言&#xff08;VC&#xff09;被动效果技能本体AS 珠子 回到人物索引 基本资料 NS(4★)NS(5★)AS卡池 (Ver 2.0.20)卡池 (Ver 2.0.20)卡池 (Ver 2.6.10)—ジャッジメントの書&#xff08;ナダラ火山VH古…

chatgpt赋能python:PythonRemovebg:一个强大的背景去除工具

Python Removebg&#xff1a;一个强大的背景去除工具 随着社交媒体和电子商务的发展&#xff0c;完美的照片处理越来越重要。其中最重要的一步就是去除背景&#xff0c;从而让照片更加专业。传统的方法需要使用复杂的软件和大量的时间&#xff0c;但是现在有了Python Removebg…

chatgpt赋能python:Python自动化不同账号登录

Python 自动化不同账号登录 在日常的工作中&#xff0c;我们可能需要登录多个不同的账号&#xff0c;手动一个一个输入用户名和密码费时费力。而 Python 的自动化功能可以帮助我们实现登陆多个不同账号的目的。本文将介绍使用 Python 自动化不同账号登录的方法。 简介 Pytho…

chatgpt赋能Python-pythonguanwang

Python官网SEO分析 Python是一种高级编程语言&#xff0c;被广泛应用于Web开发、数据科学、人工智能、机器学习等领域。Python官网&#xff08;https://www.python.org&#xff09;是Python语言的官方网站&#xff0c;为Python用户和开发者提供了最新的Python解释器、文档、库…

chatgpt赋能python:Python可以搜集网上资料吗?

Python 可以搜集网上资料吗&#xff1f; Python 是一种非常流行的编程语言&#xff0c;它广泛应用于数据科学、机器学习、人工智能等领域。作为一名 Python 工程师&#xff0c;我们经常需要在互联网上搜集资料来解决开发中的问题&#xff0c;但是有些人会问&#xff1a;Python…

使用Unity制作游戏AI

本文由独立游戏工作室Synnaxium Studio介绍游戏AI的概念和开发方法。本文中所有内容都是他们在开发《Radiant Blade》游戏的原型阶段所积累的经验。下面是《Radiant Blade》的演示画面。使用游戏AI的原因首先&#xff0c;我们要思考为什么要给游戏添加AI&#xff1f;长期以来&a…

【Novel AI】使用绘画AI构建unity游戏资源

请勿在商业用途中使用下面的示例图中的任何资源&#xff01; 1. 设想&#xff1a; 首先根据自己的设想&#xff0c;创造一些角色原型&#xff0c;角色特点等等 这里我根据网络上找的一些参考&#xff0c;我大概想要的是比较特别的一个警察的猫人形的动物角色&#xff1b; 2…

2023-04-27 Android APP 不同机型跳转应用权限设置页面,我这里在小米(android12)和三星(android8)上测试

一、代码 package com.xxx.bluetooth_ble.permission;import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.…

【ROM定制】Android 12 制作『MIUI官改』那点事④修改

作者&#xff1a;小谢 内容&#xff1a;「MIUI官改」的修改 时间&#xff1a;2022.10.14 机型&#xff1a;小米10 安卓&#xff1a;Android 12 版本&#xff1a;V13.0.7.0稳定版 制作『MIUI官改』③工具&#xff1a;查看链接 ————————ROM修改———————— →◆解压…

雅思小作文 / IELTS Task1

The collection of IELTS Test resources. Based on Cambridge English 9-14 Last edited by Tang at 2019.06.11 17.01 Writing Task 1&#xff08;1 or 2 days&#xff09; 为什么我认为在准备writing时&#xff0c;task1更加重要&#xff1f; 因为task1侧重描写&#…

插画培训班学费一般是多少钱

插画培训班学费一般是多少钱&#xff1f;我们看待任何问题都要全面的思考&#xff0c;所以今天将会从三个方面来解析&#xff0c;分别是如何辨别插画培训机构是否靠谱、插画网课平台有哪些、插画培训班如何避坑&#xff0c;赶紧进入精彩内容吧&#xff01; 一&#xff1a;插画培…

首席新媒体运营黎想教程:线上活动推广策划及方案解析

对运营人员来说&#xff0c;活动运营是非常重要的一部分工作内容。那么细化到在线学习平台&#xff0c;组织运营一场活动有哪些要点呢&#xff1f;活动设计上又要注意什么呢&#xff1f;本文将告诉我们答案。 现在我介绍一下&#xff0c;我是青岛艺形艺意文化传媒有限公司创始…

新媒体运营教程:策划一场成功漂亮的活动策划

作为一个运营人&#xff0c;活动推广&#xff08;活动运营&#xff09;是我们运营人在工作当中运用最多的一个推广手段&#xff0c;也是通过活动策划&#xff0c;创意策划、活动互惠、互动反馈&#xff0c;对于引流、交互等行之有效的方式。 所以&#xff0c;在营销推广中的活动…

游戏推广怎么做比较好?做游戏推广时如何选取靠谱的宣传机构

本文由鲨鱼网媒小编为你分享&#xff01; 一、游戏推广的方法 1、多平台游戏推广 游戏推广可以透过多平台进行&#xff0c;比如通过社交媒体、论坛、网站、网络广告、视频、游戏联盟等渠道进行推广。可以通过利用这些渠道进行游戏宣传&#xff0c;可以更有效地提高游戏的知名…

APP社交类项目(类微信)运营之一线上推广

项目二于上月已完成原型设计&#xff0c;内部测试&#xff0c;目前处于推广阶段。下面聊一下我们团队目前想到并正在使用的推广方式&#xff1a; 推广方式主要聚焦于线上&#xff0c; 一、上线各大android平台&#xff0c;现在各大安卓市场都有活动&#xff0c;而且都是免费的&…