前言


  在参考互联网大厂的登录、订单、提现这类对安全性操作要求较高的场景操作时发现,传输的都是密文。而为了目前项目安全,我自己负责的项目也需要这方面的技术。由于,我当前的项目是使用了前后端分离技术,即node.js做前端,spring boot做后端。于是,我开始搜索有关node.js与java实现非对称加密的资料,然而,我却没有得到一个满意的答案。因此,我有了写本篇博客的想法,并希望给用到这类技术的朋友提供帮助。

一、明文密码传输对比


首先、 构建spring boot 2.0项目

引入web依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

  

App启动类

@SpringBootApplication
public class App { public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}

控制器类,编写两个方法:

1.模拟获取登录后的用户信息

2.明文登录方法

/**
* java与node.js非对称加密
*
* 出自:http://www.cnblogs.com/goodhelper
*
* @author 刘冬
*
*/
@RestController
public class MainController { /**
* 存储用户信息
*/
private Map<String, String> users = new ConcurrentHashMap<>(); @GetMapping("getUser")
public String getUser(@RequestHeader(value = "Authorization", required = false) String token) {
if (token == null) {
return null;
}
return users.containsKey(token) ? users.get(token) : null;
} @PostMapping("login")
public Map<String, Object> login(@RequestBody Map<String, String> params) {
Map<String, Object> result = new HashMap<>();
if (!params.containsKey("account") || !params.containsKey("password")) {
result.put("success", false);
result.put("message", "请输入账号和密码");
return result;
}
if (!"123456".equals(params.get("password"))) {
result.put("success", false);
result.put("message", "密码错误");
return result;
} String token = UUID.randomUUID().toString();
users.put(token, params.get("account")); result.put("success", true);
result.put("message", "登录成功");
result.put("data", token);
return result;
}
}

其次、使用vue脚手架构建项目

安装依赖

vue init webpack demo-rsa
npm install
npm install --save axios

  

main.js入口

import Vue from 'vue'
import App from './App'
import router from './router' Vue.config.productionTip = false import axios from 'axios'
Vue.prototype.$axios = axios
axios.defaults.baseURL = '/api' new Vue({
el: '#app',
router,
components: {
App
},
template: '<App/>'
})

main.js

路由的钩子函数

import Vue from 'vue'
import Router from 'vue-router'
import Main from '@/components/Main'
import Login from '@/components/Login' Vue.use(Router)
let routes = [{
path: '/',
name: '首页',
component: Main
},
{
path: '/login',
name: '登录',
component: Login
}
] const router = new Router({
routes: routes
}) router.beforeEach((to, from, next) => {
if (to.path == '/login') {
sessionStorage.removeItem('Authorization')
} let token = sessionStorage.getItem('Authorization')
if (!token && to.path != '/login') {
next({
path: '/login'
})
return
}
next()
}) export default router;

router/index.js

登录后的页面

<template>
<div class="hello">
当前用户:{{user}}
</div>
</template> <script>
export default {
data() {
return {
user: null
}
},
mounted() {
this.$axios.get('/getUser').then(res => {
this.user = res.data
})
}
}
</script> <!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped> </style>

components/Main.vue

登录页面

<template>
<div class="hello">
<table>
<tr>
<td>
用户名:
</td>
<td>
<input type="text" v-model="form.account" />
</td>
</tr>
<tr>
<td>
密码:
</td>
<td>
<input type="password" v-model="form.password" />
</td>
</tr>
<tr>
<td>
<input type="button" value="登录" @click="login" />
</td>
<td>
<font v-if="message">{{message}}</font>
</td>
</tr>
</table>
</div>
</template> <script> export default {
data() {
return {
message: null,
form: {
account: null,
password: null
}
}
},
methods: {
//明文登录
login() {
this.message = null
this.$axios.post('/login', this.form).then(res => {
if (!res.data.success) {
this.message = res.data.message
return
}
let token = res.data.data
sessionStorage.setItem('Authorization', token)
this.$axios.defaults.headers.common['Authorization'] = token
this.$router.push({
path: '/'
});
})
}
}
}
</script> <style scoped> </style>

设置开发模式反向代理 ,便于js跨域

 proxyTable: {
'/api': {
target: 'http://localhost:8080/',
changeOrigin: true,
pathRewrite: {
'^/api': '/'
}
}
}

输入用户名和密码

观察得知,传递的密码是明文

如果打开浏览器的调试模式,就能看到输入的密码。这无疑会导致系统的不安全。并且,在非https协议下,传输的密码也有被截取风险。

二、实现密文登录


思路是:

首先、java后端生成公私钥对。node.js前端调用获取公钥的方法,然后对密码进行公钥加密,再把加密过的密文发送到java后端。最后,java后端用私钥对密文解密。

俗称:公钥加密,私钥加密。而这就是非对称加密的流程。

在spring boot项目中的pom.xml引入bouncycastle

        <dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.60</version>
</dependency>

完整的pom.xml文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.demo</groupId>
<artifactId>rsa</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>rsa</name>
<description>java与node.js非对称加密</description> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency> <!-- bouncycastle -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.60</version>
</dependency> </dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>

pom.xml

在MainController类中增加如下代码:

1.生成公私钥对,模拟session实现存储私钥,返回公钥

2.实现私钥解密

    /**
* 存储session私钥
*/
private Map<String, String> session = new ConcurrentHashMap<>(); static {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
} /**
* 获取session公钥
*
* @return
*/
@GetMapping("getSession")
public Map<String, String> getSession() throws Exception {
String sessionId = UUID.randomUUID().toString();
Map<String, String> result = new HashMap<>();
result.put("sessionId", sessionId); String algorithm = "RSA";
String privateKey = null, publicKey = null; KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(algorithm);
keyPairGen.initialize(512);
KeyPair keyPair = keyPairGen.generateKeyPair(); byte[] encoded = keyPair.getPrivate().getEncoded();
PrivateKeyInfo pkInfo = PrivateKeyInfo.getInstance(encoded);
ASN1Encodable encodable = pkInfo.parsePrivateKey();
ASN1Primitive primitive = encodable.toASN1Primitive();
byte[] privateKeyPKCS1 = primitive.getEncoded();
PemObject pemObject = new PemObject("RSA PRIVATE KEY", privateKeyPKCS1);
try (StringWriter stringWriter = new StringWriter()) {
try (PemWriter pemWriter = new PemWriter(stringWriter)) {
pemWriter.writeObject(pemObject);
pemWriter.flush();
String pemString = stringWriter.toString();
privateKey = pemString;
}
} encoded = keyPair.getPublic().getEncoded();
SubjectPublicKeyInfo spkInfo = SubjectPublicKeyInfo.getInstance(encoded);
primitive = spkInfo.parsePublicKey();
byte[] publicKeyPKCS1 = primitive.getEncoded(); pemObject = new PemObject("RSA PUBLIC KEY", publicKeyPKCS1);
try (StringWriter stringWriter = new StringWriter()) {
try (PemWriter pemWriter = new PemWriter(stringWriter)) {
pemWriter.writeObject(pemObject);
pemWriter.flush();
String pemString = stringWriter.toString();
publicKey = pemString;
}
} // 记录私钥
session.put(sessionId, privateKey);
// 返回公钥
result.put("publicKey", publicKey); return result;
} @SuppressWarnings("unchecked")
@PostMapping("loginByEncrypt")
public Map<String, Object> loginByEncrypt(@RequestBody Map<String, String> params) {
Map<String, Object> result = new HashMap<>(); if (!params.containsKey("sessionId")) {
result.put("success", false);
result.put("message", "sessionId是必填参数");
return result;
} if (!params.containsKey("playload")) {
result.put("success", false);
result.put("message", "playload是必填参数");
return result;
} String sessionId = params.get("sessionId"); if (!session.containsKey(sessionId)) {
result.put("success", false);
result.put("message", "无效session");
return result;
} Map<String, String> json = null;
try {
String privateKey = session.get(sessionId);
String playload = params.get("playload");
String text = decrypt(playload, privateKey); ObjectMapper mapper = new ObjectMapper();
json = mapper.readValue(text, Map.class);
} catch (Exception e) {
e.printStackTrace();
} if (json == null) {
result.put("success", false);
result.put("message", "非法请求");
return result;
} if (!json.containsKey("account") || !json.containsKey("password")) {
result.put("success", false);
result.put("message", "请输入账号和密码");
return result;
}
if (!"123456".equals(json.get("password"))) {
result.put("success", false);
result.put("message", "密码错误");
return result;
} String token = UUID.randomUUID().toString();
users.put(token, json.get("account")); result.put("success", true);
result.put("message", "登录成功");
result.put("data", token);
return result;
} /**
* 私钥解密
*
* @param encode
* @param privateKey
* @return
* @throws Exception
*/
private String decrypt(String text, String privateKey) throws Exception {
String algorithm = "RSA";
String keyText = privateKey.split("-----")[2].replaceAll("\n", "").replaceAll("\r", "");
byte[] bytes = Base64.decode(keyText.getBytes());
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(bytes);
PrivateKey key = keyFactory.generatePrivate(privateKeySpec); Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, key); byte[] doFinal = cipher.doFinal(Base64.decode(text));
return new String(doFinal, "utf-8");
}

完整的MainController为:

package com.demo.rsa;

import java.io.StringWriter;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.Security;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import javax.crypto.Cipher; import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemWriter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController; import com.fasterxml.jackson.databind.ObjectMapper; /**
* java与node.js非对称加密
*
* 出自:http://www.cnblogs.com/goodhelper
*
* @author 刘冬
*
*/
@RestController
public class MainController { /**
* 存储用户信息
*/
private Map<String, String> users = new ConcurrentHashMap<>(); /**
* 存储session私钥
*/
private Map<String, String> session = new ConcurrentHashMap<>(); static {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
} @GetMapping("getUser")
public String getUser(@RequestHeader(value = "Authorization", required = false) String token) {
if (token == null) {
return null;
}
return users.containsKey(token) ? users.get(token) : null;
} @PostMapping("login")
@Deprecated
public Map<String, Object> login(@RequestBody Map<String, String> params) {
Map<String, Object> result = new HashMap<>();
if (!params.containsKey("account") || !params.containsKey("password")) {
result.put("success", false);
result.put("message", "请输入账号和密码");
return result;
}
if (!"123456".equals(params.get("password"))) {
result.put("success", false);
result.put("message", "密码错误");
return result;
} String token = UUID.randomUUID().toString();
users.put(token, params.get("account")); result.put("success", true);
result.put("message", "登录成功");
result.put("data", token);
return result;
} /**
* 获取session公钥
*
* @return
*/
@GetMapping("getSession")
public Map<String, String> getSession() throws Exception {
String sessionId = UUID.randomUUID().toString();
Map<String, String> result = new HashMap<>();
result.put("sessionId", sessionId); String algorithm = "RSA";
String privateKey = null, publicKey = null; KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(algorithm);
keyPairGen.initialize(512);
KeyPair keyPair = keyPairGen.generateKeyPair(); byte[] encoded = keyPair.getPrivate().getEncoded();
PrivateKeyInfo pkInfo = PrivateKeyInfo.getInstance(encoded);
ASN1Encodable encodable = pkInfo.parsePrivateKey();
ASN1Primitive primitive = encodable.toASN1Primitive();
byte[] privateKeyPKCS1 = primitive.getEncoded();
PemObject pemObject = new PemObject("RSA PRIVATE KEY", privateKeyPKCS1);
try (StringWriter stringWriter = new StringWriter()) {
try (PemWriter pemWriter = new PemWriter(stringWriter)) {
pemWriter.writeObject(pemObject);
pemWriter.flush();
String pemString = stringWriter.toString();
privateKey = pemString;
}
} encoded = keyPair.getPublic().getEncoded();
SubjectPublicKeyInfo spkInfo = SubjectPublicKeyInfo.getInstance(encoded);
primitive = spkInfo.parsePublicKey();
byte[] publicKeyPKCS1 = primitive.getEncoded(); pemObject = new PemObject("RSA PUBLIC KEY", publicKeyPKCS1);
try (StringWriter stringWriter = new StringWriter()) {
try (PemWriter pemWriter = new PemWriter(stringWriter)) {
pemWriter.writeObject(pemObject);
pemWriter.flush();
String pemString = stringWriter.toString();
publicKey = pemString;
}
} // 记录私钥
session.put(sessionId, privateKey);
// 返回公钥
result.put("publicKey", publicKey); return result;
} @SuppressWarnings("unchecked")
@PostMapping("loginByEncrypt")
public Map<String, Object> loginByEncrypt(@RequestBody Map<String, String> params) {
Map<String, Object> result = new HashMap<>(); if (!params.containsKey("sessionId")) {
result.put("success", false);
result.put("message", "sessionId是必填参数");
return result;
} if (!params.containsKey("playload")) {
result.put("success", false);
result.put("message", "playload是必填参数");
return result;
} String sessionId = params.get("sessionId"); if (!session.containsKey(sessionId)) {
result.put("success", false);
result.put("message", "无效session");
return result;
} Map<String, String> json = null;
try {
String privateKey = session.get(sessionId);
String playload = params.get("playload");
String text = decrypt(playload, privateKey); ObjectMapper mapper = new ObjectMapper();
json = mapper.readValue(text, Map.class);
} catch (Exception e) {
e.printStackTrace();
} if (json == null) {
result.put("success", false);
result.put("message", "非法请求");
return result;
} if (!json.containsKey("account") || !json.containsKey("password")) {
result.put("success", false);
result.put("message", "请输入账号和密码");
return result;
}
if (!"123456".equals(json.get("password"))) {
result.put("success", false);
result.put("message", "密码错误");
return result;
} String token = UUID.randomUUID().toString();
users.put(token, json.get("account")); result.put("success", true);
result.put("message", "登录成功");
result.put("data", token);
return result;
} /**
* 私钥解密
*
* @param encode
* @param privateKey
* @return
* @throws Exception
*/
private String decrypt(String text, String privateKey) throws Exception {
String algorithm = "RSA";
String keyText = privateKey.split("-----")[2].replaceAll("\n", "").replaceAll("\r", "");
byte[] bytes = Base64.decode(keyText.getBytes());
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(bytes);
PrivateKey key = keyFactory.generatePrivate(privateKeySpec); Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, key); byte[] doFinal = cipher.doFinal(Base64.decode(text));
return new String(doFinal, "utf-8");
}
}

MainController

在node.js项目中安装node-rsa依赖

npm install --save node-rsa

增加密文登录的方法:

首先,调用getSession接口获取后端生成的公钥

其次,调用node-rsa封装的方法,实现公钥加密。注意的是,需要设置密钥格式为pkcs1,否则后端解密出的字符会是乱码。

<template>
<div class="hello">
<table>
<tr>
<td>
用户名:
</td>
<td>
<input type="text" v-model="form.account" />
</td>
</tr>
<tr>
<td>
密码:
</td>
<td>
<input type="password" v-model="form.password" />
</td>
</tr>
<tr>
<td>
<input type="button" value="登录" @click="loginByEncrypt" />
</td>
<td>
<font v-if="message">{{message}}</font>
</td>
</tr>
</table>
</div>
</template> <script>
import NodeRSA from 'node-rsa' export default {
data() {
return {
message: null,
sessionId: null,
publicKey: null,
form: {
account: null,
password: null
}
}
},
methods: {
//明文登录
login() {
this.message = null
this.$axios.post('/login', this.form).then(res => {
if (!res.data.success) {
this.message = res.data.message
return
}
let token = res.data.data
sessionStorage.setItem('Authorization', token)
this.$axios.defaults.headers.common['Authorization'] = token
this.$router.push({
path: '/'
});
})
},
//获取session公钥
getSession() {
this.$axios.get('/getSession', this.form).then(res => {
this.sessionId = res.data.sessionId
this.publicKey = res.data.publicKey
})
},
//密文登录
loginByEncrypt() {
let key = new NodeRSA(this.publicKey)
key.setOptions({
encryptionScheme: 'pkcs1'
}) this.message = null
let playload = key.encrypt(JSON.stringify(this.form), 'base64', 'utf8')
let param = {
sessionId: this.sessionId,
playload: playload
}
this.$axios.post('/loginByEncrypt', param).then(res => {
if (!res.data.success) {
this.message = res.data.message
return
}
let token = res.data.data
sessionStorage.setItem('Authorization', token)
this.$axios.defaults.headers.common['Authorization'] = token
this.$router.push({
path: '/'
});
})
}
},
mounted() {
this.getSession()
}
}
</script> <style scoped> </style>

获取公钥的效果:

传输密文的效果:


好了,到这里,java结合node.js非对称加密的密文登录传参就实现了。不过,这篇博客仅仅是个例子。如果是在正式项目中,则需要考虑很多问题,如,私钥存在数据库或redis,而非java内存中。

参考:https://www.npmjs.com/package/node-rsa

代码地址:https://github.com/carter659/java-node-rsa-demo

如果你觉得我的博客对你有帮助,可以给我点儿打赏,左侧微信,右侧支付宝。

有可能就是你的一点打赏会让我的博客写的更好:)

玩转spring boot系列目录

作者:刘冬.NET 博客地址:http://www.cnblogs.com/GoodHelper/ 欢迎转载,但须保留版权

java结合node.js非对称加密,实现密文登录传参——让前后端分离的项目更安全的更多相关文章

  1. 如何在node和vue前后端分离的项目中使用极客验证,用node的方式

    1.用express的脚手架和vue-cli的脚手架搭建的项目目录如下图 2.在vue-client的src/component新建一个login.vue文件,作为登录页面,代码如下 <temp ...

  2. 前后端分离时代,Java 程序员的变与不变!

    事情的起因是这样的,有个星球的小伙伴向邀请松哥在知乎上回答一个问题,原题是: 前后端分离的时代,Java后台程序员的技术建议? 松哥认真看了下这个问题,感觉对于初次接触前后端分离的小伙伴来说,可能都会 ...

  3. Java 前后端分离项目:微人事

    本文适合刚学习完 Java 语言基础的人群,跟着本文可了解和运行项目,本示例是在 Windows 操作系统下演示. 本文作者:HelloGitHub-秦人 大家好!这里是 HelloGitHub 推出 ...

  4. ASP.NET Core 实战:使用 ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目

    一.前言 这几年前端的发展速度就像坐上了火箭,各种的框架一个接一个的出现,需要学习的东西越来越多,分工也越来越细,作为一个 .NET Web 程序猿,多了解了解行业的发展,让自己扩展出新的技能树,对自 ...

  5. List多个字段标识过滤 IIS发布.net core mvc web站点 ASP.NET Core 实战:构建带有版本控制的 API 接口 ASP.NET Core 实战:使用 ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目 Using AutoFac

    List多个字段标识过滤 class Program{  public static void Main(string[] args) { List<T> list = new List& ...

  6. 一个Java程序猿眼中的前后端分离以及Vue.js入门

    松哥的书里边,其实有涉及到 Vue,但是并没有详细说过,原因很简单,Vue 的资料都是中文的,把 Vue.js 官网的资料从头到尾浏览一遍该懂的基本就懂了,个人感觉这个是最好的 Vue.js 学习资料 ...

  7. SpringBoot使用SpringSecurity搭建基于非对称加密的JWT及前后端分离的搭建

    SpringBoot使用SpringSecurity搭建基于非对称加密的JWT及前后端分离的搭建 - lhc0512的博客 - CSDN博客 https://blog.csdn.net/lhc0512 ...

  8. java前后端分离是否会成为趋势

    现在项目当中使用的是springboot+springcloud,这套框架也用了半年了,springboot是spring4.0的升级版,简化了springmvc的xml配置,是spring家族中目前 ...

  9. Node前后端分离基本概括

    首先从一个重要的概念“模板”说起. 广义上来说,web中的模板就是填充数据后可以生成文件的页面. 严格意义上来说,应该是模板引擎利用特定格式的文件和所提供的数据编译生成页面.模板大致分为前端模板(如e ...

随机推荐

  1. C#中异步调用示例与详解

    using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServi ...

  2. Angularjs判断页面是否已经渲染结束(动态给标签长度)

    相信大家都会碰到这样的问题.页面循环li.但是因为个数不知道.没有办法给li设置固定宽度.那么这时就需要动态计算数据长度并动态改变li的宽度 <!--周边信息--> <div cla ...

  3. mysql完整性约束

    第一:完整性约束介绍 为了防止不符合规范的数据进入数据库,在用户对数据进行插入.修改.删除等操作时,DBMS自动按照一定的约束条件对数据进行监测,使不符合规范的数据不能写入数据库,以确保数据库中存储的 ...

  4. CentOS裸机环境下安装php-7.3.1

    安装步骤如下 安装必要的软件 获取源码 编译安装 安装过程可能遇到的一些问题 编译参数详解 安装步骤如下 安装必要的软件 yum install -y autoconf automake libtoo ...

  5. Codeforces Round #547 (Div. 3)

    我老人家走了四公里吃个汉堡还没吃成.垃圾肯德基.垃圾春分半价桶. 蜜雪冰城百香果加冰+烤串真是爽死了.原来二十多块钱可以吃的这么爽. A: #include <bits/stdc++.h> ...

  6. hh

    1

  7. python中剔除字典重复项,可以使用集合(set)。

    使用集合(set)剔除字典中的重复项(value). 1)具体例子: #甲乙丙丁使用的编程语言programming_languages = { '甲':'java', '乙':'python', ' ...

  8. MySQL执行计划复习

    MySQL执行计划分析 Ⅰ.认识执行计划的每个字段 (root@localhost) [(none)]> desc select 1; +----+-------------+-------+- ...

  9. webToImage (网页转图片)模块试用分享

    模块介绍: 本模块封装了把 webview 转换成图片的功能.调用本模块的transImage接口,可把当前 webview显示的内容转换成一张图片.注意,本模块只能把当前的webview页面转换为图 ...

  10. Ionic3多个自定义过滤器--管道(pipe)

    往往我们创建自定义管道一般都不止只会创建一个自定义管道,和自定义组件类似,创建多个方式如下. 一.命令行生成管道 ionic g pipe formateDate ionic g pipemoneyD ...