使用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