使用Spring-boot 及 Vue 簡易架設自動部屬電商網站,包含中文資料庫CRUD功能。
使用Vuex以及spring security簡易登入系統做no session, stateless的簡易登入。
專案架構採取前後端分離,使用maven-frontend-plugin確保每個部屬或測試環境的node 與 npm 版本相同,
新環境運行不需要全域安裝node及npm (前端開發時自行全域安裝node與npm)
spring-boot-vuejs
├─┬ backend → 後端 module (Spring Boot)
│ ├── src
│ └── pom.xml → 後端 dependency
├─┬ frontend → 前端 module (Vue)
│ ├── src
│ └── pom.xml → 前端 frontend-maven-plugin
└── pom.xml → Maven parent pom
framework or dependency | description |
---|---|
SpringBoot | MVC框架 |
SpringSecurity | 認證授權框架 |
Spring-Boot Devtools | 開發者工具(更改後端代碼免重啟) |
Spring-Data-Rest | 提供Spring-Data-Repository endpoints |
Spring Boot Actuator | 內部監控(網站健康狀況、Bean生命週期及endpoints) |
Spring Data JPA | ORM(可連接多個datasource、減少sql語句、entity對應資料庫) |
PostgreSQL | 產品環境使用遠端資料庫連接 |
MySQL-connecter-java | 連接本機MySQL資料庫 |
Heroku | 雲端部屬服務 |
Travis CI | 持續整合(unit Test) |
Lombok | 簡化封裝工具(免下getter setter) |
framework or package | description |
---|---|
Vue | 前端框架 |
Vue-router | 路由框架 |
Vue-cli | webpack based開發懶人包(快速創建默認配置專案) |
Vuex | 全局狀態管理框架 |
Axios | 前端HTTP框架 |
Bootstrap-vue | bootstrap-vue整合 |
Jest | 前端單元測試 |
nightwatch | 端對端測試 |
使用maven-resource-plugin將frontend/target/dist內檔案複製到
backend/src/main/resources/public
# Docker multi-stage build
# 1. Building the App with Maven
FROM maven:3-jdk-11
ADD . /springvuedir
WORKDIR /springvuedir
# Just echo so we can see, if everything is there :)
RUN ls -l
# Run Maven build
RUN mvn clean install
# Just using the build artifact and then removing the build-container
FROM openjdk:11-jdk
MAINTAINER keepprogress
VOLUME /tmp
# Add Spring Boot app.jar to Container
COPY --from=0 "/springvuedir/backend/target/backend-1.0.0.jar" app.jar
ENV JAVA_OPTS=""
# Fire up our Spring Boot app by default
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar" ]
建立.travis.yml檔案
before_install 配置與application-dev.properties 對應之datasource
-------------------------------------------------------------------------------
language: java
jdk:
- openjdk8
- openjdk9
- openjdk11
service:
- mysql
before_install:
- mysql -u root -e 'CREATE SCHEMA IF NOT EXISTS `full-stack-ecommerce`;'
- mysql -u root -e "CREATE USER IF NOT EXISTS 'ecommerce'@'localhost' IDENTIFIED BY 'ecommerce';"
- mysql -u root -e "GRANT ALL PRIVILEGES ON * . * TO 'ecommerce'@'localhost';"
- mysql -u root -e "USE `full-stack-ecommerce`;"
script: mvn clean install
cache:
directories:
- node_modules
-------------------------------------------------------------------------------
勾選等待CI PASS
在backend/src/main/java/com/keepprogress/backend/configuration創立 WebSecurityConfiguration.java檔 關閉SpringSecurity內建 CSRF token 前端不接Token 設定/api/secured需驗證
package com.keepprogress.backend.configuration;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // No session will be created or used by spring security
.and()
.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/magic-api/**").permitAll()
.antMatchers("/api/hello").permitAll()
.antMatchers("/api/user/**").permitAll() // allow every URI, that begins with '/api/user/'
.antMatchers("/api/secured").authenticated()
//.anyRequest().authenticated() // protect all other requests
.and()
.csrf().disable(); // disable cross site request forgery, as we don't use cookies - otherwise ALL PUT, POST, DELETE will get HTTP 403!
}
}
module.exports = {
// proxy all webpack dev-server requests starting with /api
// to our Spring Boot backend (localhost:8098) using http-proxy-middleware
// see https://cli.vuejs.org/config/#devserver-proxy
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8098',
ws: true,
changeOrigin: true
}
}
},
...
}
--------------------------------------------------------------------------------
在vue根目錄創建vue.config.js
https://cli.vuejs.org/zh/config/#devserver-proxy
須注意使用devserver-proxy跳轉到localhost:8098時
axios的api baseURL 配置只需維持預設值(8080),設定8098會CORS失敗
在backend/src/main/resources
有三個application properties檔案
application.properties → 存放共通設定
application-dev.properties → 存放本機資料庫(MySQL)連線properties
application-prod.properties → 存放遠端資料庫(PostgreSQL)連線properties
application.properties 內
以
spring.profiles.active = dev or prod
做切換
在spring.datasource.url加上 ?useUnicode=true&characterEncoding=UTF-8
#datasource.url=postgres://lfjmhfcpbxtzjh:46fc048e051f0d530e492f17a9042422c3797ecc882de4b5bbc5106eac0b0df8@ec2-54-166-242-77.compute-1.amazonaws.com:5432/d1p7k40m672pvb
##check ok!
spring.datasource.url=jdbc:postgresql://ec2-54-166-242-77.compute-1.amazonaws.com:5432/d1p7k40m672pvb?useUnicode=true&characterEncoding=UTF-8
spring.datasource.username=lfjmhfcpbxtzjh
spring.datasource.password=46fc048e051f0d530e492f17a9042422c3797ecc882de4b5bbc5106eac0b0df8
spring.datasource.type=org.apache.tomcat.jdbc.pool.DataSource
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
Entity內
@Id
@GeneratedValue(
strategy = GenerationType.AUTO,
generator="native"
)
@GenericGenerator(
name="native",
strategy = "native"
)
...
參考 https://vladmihalcea.com/why-should-not-use-the-auto-jpa-generationtype-with-mysql-and-hibernate/
根目錄創建 nightwatch.conf.js
const chrome = require('chromedriver')
module.exports = {
src_folders: ['tests/e2e/youtubee2e'],
page_objects_path: ['tests/e2e/youtubee2e/pageobj'],
webdriver: {
start_process: true,
server_path: chrome.path,
port: 9500
},
test_settings: {
default: {
desiredCapabilities: {
browserName: 'chrome'
}
}
}
}
src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import api from '@/utils/backend-api.js'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
loginSuccess: false,
loginError: false,
userName: null,
userPass: null
},
actions: {
login ({ commit }, { user, password }) {
return new Promise((resolve, reject) => {
console.log("Accessing backend with user: '" + user)
api.getSecured(user, password)
.then(response => {
console.log("Response: '" + response.data + "' with Statuscode " + response.status)
if (response.status === 200) {
console.log('Login successful')
// place the loginSuccess state into our vuex store
commit('loginSuccess', {
userName: user,
userPass: password
})
}
resolve(response)
})
.catch(error => {
console.log('Error: ' + error)
// place the loginError state into our vuex store
commit('loginError', {
userName: user
})
reject(new Error('Invalid credentials!'))
})
})
}
},
mutations: {
loginSuccess (state, payload) {
state.loginSuccess = true
state.username = payload.userName
},
loginError (state, payload) {
state.loginError = true
state.userName = payload.userName
}
},
getters: {
isLoggedIn: state => state.loginSuccess,
hasLoginErrored: state => state.loginError
}
})
在src/router/index.js
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// this route requires auth, check if logged in
// if not, redirect to login page.
console.log('Needs Authorization Here!')
if (!store.getters.isLoggedIn) {
next({
path: '/login'
})
} else {
next()
}
} else {
next() // make sure to always call next()!
}
})
安裝Java8
參考manual
https://docs.aws.amazon.com/corretto/latest/corretto-8-ug/windows-7-install.html
安裝MySQL
參照MySQL manual
https://dev.mysql.com/doc/mysql-installation-excerpt/5.7/en/windows-installation.html
全域安裝nodejs 及 npm
https://nodejs.org/en/
安裝nodejs以後會順便安裝npm
安裝maven (需先安裝java8)
https://maven.apache.org/install.html
安裝git bash
https://git-scm.com/downloads
Git PATH設定 選擇 use git from the windows command prompt
MySQL資料庫編碼改成utf8mb4(中文化)
參考
https://stackoverflow.com/questions/3513773/change-mysql-default-character-set-to-utf-8-in-my-cnf/30044721#30044721
my.inf檔案 通常會在programData/MySQL/MySQLServerX.X/my.inf
需先設定顯示隱藏檔案
完成後進MySQL Command Line Client
輸入show variables like 'char%'
除了character_set_system會是utf8 及 character_set_filesystem 會是binary
其他都是utf8mb4 就成功了
安裝git
sudo yum install git
安裝java8
sudo yum install java-1.8.0-openjdk
安裝wget
sudo yum install wget
安裝grep
sudo yum install grep
安裝maven
sudo yum install maven
安裝MySQL
參考MySQL manual
https://dev.mysql.com/doc/refman/8.0/en/charset.html
改編碼utf8mb4參考
https://stackoverflow.com/questions/3513773/change-mysql-default-character-set-to-utf-8-in-my-cnf/30044721#30044721
CREATE USER 'ecommerce'@'localhost' IDENTIFIED BY 'ecommerce';
GRANT ALL PRIVILEGES ON * . * TO 'ecommerce'@'localhost';
ALTER USER 'ecommerce'@'localhost' IDENTIFIED WITH mysql_native_password BY 'ecommerce';
-- -----------------------------------------------------
-- Schema full-stack-ecommerce
-- -----------------------------------------------------
DROP SCHEMA IF EXISTS `full-stack-ecommerce`;
CREATE SCHEMA `full-stack-ecommerce`;
USE `full-stack-ecommerce` ;
-- -----------------------------------------------------
-- 由於使用ddl.auto = update 可以不用手動創建table
-- -----------------------------------------------------
使用管理員權限執行cmd
git clone https://github.com/keepprogress/spring-vue-test.git
cd .../spring-vue-test-master
mvn clean install
mvn -pl backend spring-boot:run
cd .../spring-vue-test-master/frontend
npm run serve
git clone https://github.com/keepprogress/spring-vue-test.git
cd .../spring-vue-test-master
mvn clean install
mvn -pl backend spring-boot:run
cd .../spring-vue-test-master/frontend
npm run test