pkcs1解密 springboot_Vue、SprinBoot开发运维的一些坑和知识集锦

一、完美解决Vue2.0+Axios开发生产环境跨域问题

由于博主主要是做后端开发和自动化运维的,因此,前端基本面向同学和搜索引擎编程...这次彻底搞出了一个简洁优雅的Vue和Axios配合的跨域方案,适合开发环境和生产环境!

(1)在config/index.js中配置开发环境跨域

proxyTable: {

'/api': {

target: 'https://211.64.32.228:8899/',

secure: false,

changeOrigin: true,

pathRewrite: {

'^/api': ''

},

headers: {

Referer: 'https://211.64.32.228:8899'

}

}

}

(2)在main.js中配置自动选择

import axios from 'axios'

import QS from 'qs'

Vue.prototype.$axios = axios

Vue.prototype.$qs = QS

Vue.prototype.baseUrl = process.env.NODE_ENV === "production" ? "https://211.64.32.228:8899" : "/api"

(3)在Vue文件中使用Axios

this.axios({

method: 'post',

url: this.baseUrl + '/helloworld',

data: {},

headers: {}

}).then((response) => {

// do some

}).catch((error) => {

// do some

});

(4)SpringBoot配置允许跨域

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.cors.CorsConfiguration;

import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import org.springframework.web.filter.CorsFilter;

@Configuration

public class CorsConfig {

private CorsConfiguration buildConfig() {

CorsConfiguration corsConfiguration = new CorsConfiguration();

corsConfiguration.addAllowedOrigin("*");

corsConfiguration.addAllowedHeader("*");

corsConfiguration.addAllowedMethod("*");

return corsConfiguration;

}

@Bean

public CorsFilter corsFilter() {

UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

source.registerCorsConfiguration("/**", buildConfig());

return new CorsFilter(source);

}

}

二、SpringBoot中AES+Base64加解密用户登录凭据

这年头,md5是能反解的,再老也不能掉牙呀..

import org.apache.tomcat.util.codec.binary.Base64;

import javax.crypto.Cipher;

import javax.crypto.spec.SecretKeySpec;

import java.nio.charset.StandardCharsets;

// 使用方法:

// PasswordUtil.Encrypt(String)

// PasswordUtil.Decrypt(String)

public class PasswordUtil {

// openssl rand -hex 16

private static String salt = "38350e78e96b83e894b59cc9953af122";

public static String Encrypt(String password) throws Exception {

byte[] raw = salt.getBytes(StandardCharsets.UTF_8);

SecretKeySpec sRawSpec = new SecretKeySpec(raw, "AES");

Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");

cipher.init(Cipher.ENCRYPT_MODE, sRawSpec);

byte[] encrypted = cipher.doFinal(password.getBytes(StandardCharsets.UTF_8));

return new Base64().encodeToString(encrypted);

}

public static String Decrypt(String password) throws Exception{

byte[] raw = salt.getBytes(StandardCharsets.UTF_8);

SecretKeySpec sRawSpec = new SecretKeySpec(raw, "AES");

Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");

cipher.init(Cipher.DECRYPT_MODE, sRawSpec);

byte[] encrypted = new Base64().decode(password);

byte[] original = cipher.doFinal(encrypted);

return new String(original, StandardCharsets.UTF_8);

}

}

三、纯CSS自定义超简洁文件上传控件input

主要是解决自定义CSS样式问题和Vue上传文件的问题...注释就不写了,静下心来稍微一看就懂!

{{uploadTips}}

export default {

name: "Upload",

data: function () {

return {

uploadImageObject: '',

isProcessUpload: false,

uploadTips: '点击上传',

uploadTipsStyle: {

'color': 'gray'

}

}

},

mounted() {

this.$store.dispatch('commitNormalStepNumber', 2)

},

methods: {

handleUploadDemoButtonChange: function (e) {

if ((e.target.files[0].size / 1024) >= 400) {

this.$message.error('上传的文件超过指定的大小, 请重新选择');

} else {

this.uploadImageObject = e.target.files[0];

this.uploadTips = e.target.files[0].name;

this.uploadTipsStyle.color = '#409EFF';

}

},

handleProcessUpload: function () {

this.isProcessUpload = true;

// 使用FormData解决POST远程API出现获取不到参数问题

let formData = new FormData();

formData.append('uuid', this.$store.getters.getFormUsername);

formData.append('file', this.uploadImageObject);

this.$axios({

url: this.baseUrl + '/upload/image',

method: 'post',

headers: {

'Content-Type': 'multipart/form-data',

token: this.$store.getters.getToken

},

data: formData

}).then((response) => {

if (response.data === "OK") {

this.isProcessUpload = false;

this.$router.push({

path: '/finish'

});

} else if (response.data === "UNAUTHORIZED"){

this.$message.error('请登录后重试');

} else if (response.data === "INTERNAL_SERVER_ERROR") {

this.$message.error('很抱歉, 我们发生了一些错误');

} else if (response.data === "BAD_REQUEST") {

this.$message.error('你的请求有误, 文件可能有点儿问题');

} else {

this.$message.error('产生了无法预知的错误, 请重新登陆');

console.log(response.data)

}

}).catch((err) => {

this.$message.error('网络请求出错');

console.log(err)

});

this.isProcessUpload = false;

}

}

}

.upload {

width: 50%;

margin: 0 auto;

padding-top: 35px;

}

.upload-button {

padding-top: 25px;

text-align: center;

}

.upload-demo-button {

width: 349px;

height: 149px;

opacity: 0;

}

.upload-demo-button:hover {

cursor: pointer;

}

.upload-demo-show {

border-radius: 5px;

border: lightgray 1px dashed;

width: 350px;

height: 150px;

margin: 0 auto;

position: relative;

}

.upload-btn-icon {

position: absolute;

top: 15%;

left: 40%;

font-size: 50pt;

z-index: -1;

}

.upload-demo-text {

z-index: -1;

position: absolute;

top: 58%;

width: 250px;

text-align: center;

left: 50%;

font-size: 10pt;

margin-left: -125px;

}

四、Vuex最佳实践

(1)定义store/index.js,事实上应该事先模块化,但是我太懒了。

import Vue from 'vue';

import Vuex from 'vuex';

Vue.use(Vuex);

const state = {

token: ''

};

const getters = {

getToken(state) {

return state.token

}

};

const mutations = {

setToken(state, token) {

state.token = token

}

};

const actions = {

commitToken({commit}, token) {

return commit('setToken', token)

}

};

const store = new Vuex.Store(

{

state,

getters,

mutations,

actions

}

);

export default store;

(2)在main.js中引用

import store from './store'

/* eslint-disable no-new */

new Vue({

el: '#app',

router,

components: {App},

template: '',

store

})

(3)在Vue组件中引用

this.$store.dispatch('commitToken', value); // 向Store中存储数据

this.$store.getters.getToken; // 读取Store中的数据

五、Vue-router跳转

然而,官方文档是写的很明确的,但是我懒得翻官方文档...

this.$router.push({

path: '/normal'

});

六、Nginx配合SpringBoot实现HTTPS强转和API网关负载均衡

user nginx;

worker_processes 16;

error_log logs/error.log;

pid logs/nginx.pid;

events {

worker_connections 1024;

}

http {

include mime.types;

default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '

'$status $body_bytes_sent "$http_referer" '

'"$http_user_agent" "$http_x_forwarded_for"';

access_log logs/access.log main;

sendfile on;

keepalive_timeout 65;

gzip on;

// 设置反向代理

upstream apiserver {

server 127.0.0.1:8090 weight=1;

server 127.0.0.1:8091 weight=1;

server 127.0.0.1:8092 weight=1;

server 127.0.0.1:8093 weight=1;

}

server {

listen 80;

server_name upload-image;

// 设置HTTPS强转

rewrite ^(.*)$ https://$host$1 permanent;

}

// API接口使用HTTPS

server {

listen 8899 ssl;

server_name upload-image-api;

// 配置HTTPS

ssl_certificate ../ssl/server.crt;

ssl_certificate_key ../ssl/server.key;

ssl_session_cache shared:SSL:1m;

ssl_session_timeout 5m;

ssl_ciphers ALL:!DH:!EXPORT:!RC4:+HIGH:+MEDIUM:-LOW:!aNULL:!eNULL;

ssl_prefer_server_ciphers on;

// 添加支持的HTTPS协议

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

location / {

proxy_pass http://apiserver;

}

}

server {

// 将前端静态分发设置跳转到该接口

listen 443 ssl;

server_name upload-image-ssl;

ssl_certificate ../ssl/server.crt;

ssl_certificate_key ../ssl/server.key;

ssl_session_cache shared:SSL:1m;

ssl_session_timeout 5m;

ssl_ciphers ALL:!DH:!EXPORT:!RC4:+HIGH:+MEDIUM:-LOW:!aNULL:!eNULL;

ssl_prefer_server_ciphers on;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

location / {

root html;

index index.html index.htm;

}

error_page 404 /404.html;

error_page 500 502 503 504 /50x.html;

location = /50x.html {

root html;

}

}

}

七、Vue组件水平垂直都居中

就是这个问题,我一直都记不住怎么做,但是我一直都能百度到,连Google都不用...

(1) index.html,设置在style标签

html, body {

margin: 0;

padding: 0;

}

(2) 组件样式

.box {

top: 50%;

left: 50%;

position: absolute;

transform: translate(-50%, -50%);

min-width: 450px;

max-width: 550px;

min-height: 500px;

max-height: 550px;

}

八、Vue与PC端摄像头交互

最近自己用Caffe训练了一个人脸识别的神经网络,以后咱也可以人脸登录了~

So,先搞定PC的Web端的摄像头再说...因为电脑拍出来的照片是不太顺眼的,因此进行了镜像翻转,

但是,你就是那么丑...是我的CSS让你变好看了,哈哈哈~

import LoginType from "../common/LoginType";

export default {

name: "LoginWithFaceDetection",

components: {LoginType},

data: function () {

return {

streamPicture: '',

faceImage: '',

hasCameraOpen: true,

hasTakePhoto: false,

faceImageFile: '',

hasLoginFormLoading: false,

clickTimes: 0

}

},

methods: {

handleFaceDetection: function () {

if (this.clickTimes === 0) {

let video = document.querySelector('video');

this.takePhoto();

this.closeCamera();

this.postFaceDetection();

console.log("Face De");

this.clickTimes = 1;

}

// TODO:显示弹窗,重复的提交

},

connectToCamera: function () {

let self = this;

navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;

if (navigator.getUserMedia) {

// 调用用户媒体设备, 访问摄像头

navigator.getUserMedia({

video: {

width: 270,

height: 270

}

}, function (stream) {

let video = document.querySelector('video');

video.srcObject = stream;

self.streamPicture = stream;

video.onloadedmetadata = function (e) {

video.play();

};

}, function (err) {

// TODO: 显示错误弹窗,不支持的媒体类型

})

} else {

// TODO:显示错误弹窗,无法访问摄像头

}

},

closeCamera: function () {

this.streamPicture.getTracks()[0].stop();

},

takePhoto: function () {

let video = document.querySelector('video');

let canvas = document.getElementById('canvas');

let context = canvas.getContext('2d');

context.drawImage(video, 0, 0, 270, 270);

let image = canvas.toDataURL('image/png');

this.hasCameraOpen = false;

this.hasTakePhoto = true;

this.faceImage = image;

this.faceImageFile = this.dataURLtoFile(image, 'face-detection.png')

},

dataURLtoFile: function (dataurl, filename) {

let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],

bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);

while (n--) {

u8arr[n] = bstr.charCodeAt(n);

}

return new File([u8arr], filename, {type: mime});

},

postFaceDetection: function () {

this.hasLoginFormLoading = true;

// TODO:发送图片进行识别

setInterval(() => {

this.hasLoginFormLoading = false;

clearInterval();

}, 5000);

}

},

mounted() {

this.connectToCamera();

},

destroyed() {

this.closeCamera();

}

}

.photo-box {

margin-top: 0;

width: 270px;

height: 270px;

border-radius: 20px;

transform: rotateY(180deg);

-webkit-transform: rotateY(180deg); /* Safari 和 Chrome */

-moz-transform: rotateY(180deg);

}

.video-box {

transform: rotateY(180deg);

-webkit-transform: rotateY(180deg); /* Safari 和 Chrome */

-moz-transform: rotateY(180deg);

margin-top: 0;

width: 270px;

height: 270px;

object-fit: contain;

border-radius: 20px;

}

.login-with-facedetection-main {

width: 450px;

height: 500px;

box-shadow: 0 0 10px lightgrey;

}

.login-with-facedetection-main-head {

width: 100%;

height: 65px;

padding-top: 35px;

text-align: center;

}

.login-with-form {

width: 270px;

margin: 0 auto;

height: 270px;

text-align: center;

background-color: #F1F3F4;

border-radius: 20px;

}

.login-with-title {

font-size: 15pt;

text-align: center;

width: 100%;

padding-top: 20px;

}

.login-with-subtitle {

font-size: 11pt;

text-align: center;

width: 100%;

padding-top: 5px;

}

.box {

top: 50%;

left: 50%;

position: absolute;

transform: translate(-50%, -50%);

min-width: 450px;

max-width: 550px;

min-height: 500px;

max-height: 550px;

}

九、Vue中组价高度自适应

让这个组件的高度总是等于浏览器窗口高度!

(1)组件绑定CSS样式

:style="sidebarStyle"

(2) JavaScript数据动态绑定

export default {

name: "Admin",

data: function () {

return {

isCollapse: true,

sidebarStyle: {

'height': ''

}

}

},

methods: {

redressHeight: function () {

this.sidebarStyle.height = window.innerHeight + 'px';

}

},

created() {

window.addEventListener('resize', this.redressHeight);

this.redressHeight();

},

destroyed() {

window.removeEventListener('resize', this.redressHeight);

}

}

持续更新中...


版权声明:本文为weixin_39894778原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。