精华内容
下载资源
问答
  • Springboot Vue Login(从零开始实现Springboot+Vue登录)

    万次阅读 多人点赞 2019-05-31 16:19:34
    最近学习使用Vue实现前端后端分离,在Github上有一个很好的开源项目:mall,正所谓百看不如一练,自己动手实现了一个Springboot+Vue的登录操作,在此记录一下踩过的坑。 文章最后补充两端的GitHub代码,之所以放在...

    一、简述

    最近学习使用Vue实现前端后端分离,在Github上有一个很好的开源项目:mall,正所谓百看不如一练,自己动手实现了一个Springboot+Vue的登录操作,在此记录一下踩过的坑。

    文章最后补充两端的GitHub代码,之所以放在最后,是因为文章写的很细致了,动手操作一下会更有帮忙,如果有很大出入可以比对原码,找找问题。

    二、开发工具

    VSCode

    IDEA

    Vue 的安装就不说了,有很多文章,但是Springboot+Vue整合的完整文章相对较少,所以我主要记录一下这两端整合时的内容。

    (Vue安装后就会有 npm 或 cnpm,相应的介绍也不说了,Vue官网可查看)

    -------------------------------Vue 开发-------------------------------

    一、打开 cmd 创建 Vue 项目,并添加 Vue 依赖的框架:

    1.创建 Vue 项目(进入自己想创建的文件夹位置,我放在 D:\VSCodeWorkSpace),创建语句 vue create vue-spring-login-summed,方向键选择创建方式,我选择的默认

    2.进入到创建的 Vue 项目目录,添加依赖框架:

    cd vue-spring-login-summed (进入到项目根目录)
    vue add element (添加 element,一个 element 风格的 UI 框架)
    npm install axios (安装 axios,用于网络请求)
    npm install vuex --save(安装 Vuex,用于管理状态)
    npm install vue-router (安装 路由,用于实现两个 Vue 页面的跳转)

    以上命令截图如下:

    1)添加 Element

    2)添加 axios

    3)添加 Vuex

    4)添加 路由

    到此相关依赖的架包添加完毕,输入 code . 打开 VSCode

    二、添加目录结构

    在 VSCode 下看到 Vue 整体项目结构如下

    现在需要创建相应功能的目录结构,进行分层开发,需要在 src 目录下创建下面几个目录

    api (网络请求接口包)
    router (路由配置包)
    store (Vuex 状态管理包)
    utils (工具包)
    views (vue 视图包,存放所有 vue 代码,可根据功能模块进行相应分包)

    创建后的目录结构如下

    三、运行项目

    现在可以运行项目了,在 VSCode 菜单栏依次选择:终端 —— 运行任务...

    这里使用的是 serve 模式,即开发模式运行的项目

    在浏览器输入:http://localhost:8080/

    这是 Vue 默认的页面,代表项目创建成功了,在进行代码开发前,先贴上项目整体结构,防止不知道在哪创建

    四、View 层代码编写

    编写三个 vue 文件:login.vue(登录页面)、success.vue(登录成功页面)、error.vue(登录失败页面)

    1.login.vue 

    代码如下(比较懒,直接从 mall 扒下来的代码,去掉了一些功能)

    <template>
      <div>
        <el-card class="login-form-layout">
          <el-form
            autocomplete="on"
            :model="loginForm"
            ref="loginForm"
            label-position="left"
          >
            <div style="text-align: center">
              <svg-icon icon-class="login-mall" style="width: 56px;height: 56px;color: #409EFF"></svg-icon>
            </div>
            <h2 class="login-title color-main">mall-admin-web</h2>
            <el-form-item prop="username">
              <el-input
                name="username"
                type="text"
                v-model="loginForm.username"
                autocomplete="on"
                placeholder="请输入用户名"
              >
                <span slot="prefix">
                  <svg-icon icon-class="user" class="color-main"></svg-icon>
                </span>
              </el-input>
            </el-form-item>
            <el-form-item prop="password">
              <el-input
                name="password"
                :type="pwdType"
                @keyup.enter.native="handleLogin"
                v-model="loginForm.password"
                autocomplete="on"
                placeholder="请输入密码"
              >
                <span slot="prefix">
                  <svg-icon icon-class="password" class="color-main"></svg-icon>
                </span>
                <span slot="suffix" @click="showPwd">
                  <svg-icon icon-class="eye" class="color-main"></svg-icon>
                </span>
              </el-input>
            </el-form-item>
            <el-form-item style="margin-bottom: 60px">
              <el-button
                style="width: 100%"
                type="primary"
                :loading="loading"
                @click.native.prevent="handleLogin"
              >登录</el-button>
            </el-form-item>
          </el-form>
        </el-card>
      </div>
    </template>
    
    <script>
    export default {
      name: "login",
      data() {
        return {
          loginForm: {
            username: "admin",
            password: "123456"
          },
          loading: false,
          pwdType: "password",
        };
      },
      methods: {
        showPwd() {
          if (this.pwdType === "password") {
            this.pwdType = "";
          } else {
            this.pwdType = "password";
          }
        },
        handleLogin() {
          this.$refs.loginForm.validate(valid => {
            if (valid) {
              this.loading = true;
              this.$store
                .dispatch("Login", this.loginForm)
                .then(response => {
                  this.loading = false;
                  let code = response.data.code;
                  if (code == 200) {
                    this.$router.push({
                      path: "/success",
                      query: { data: response.data.data }
                    });
                  } else {
                    this.$router.push({
                      path: "/error",
                      query: { message: response.data.message }
                    });
                  }
                })
                .catch(() => {
                  this.loading = false;
                });
            } else {
              // eslint-disable-next-line no-console
              console.log("参数验证不合法!");
              return false;
            }
          });
        }
      }
    };
    </script>
    
    <style scoped>
    .login-form-layout {
      position: absolute;
      left: 0;
      right: 0;
      width: 360px;
      margin: 140px auto;
      border-top: 10px solid #409eff;
    }
    
    .login-title {
      text-align: center;
    }
    
    .login-center-layout {
      background: #409eff;
      width: auto;
      height: auto;
      max-width: 100%;
      max-height: 100%;
      margin-top: 200px;
    }
    </style>
    

    2.success.vue

    <template>
      <div>
        <h1>Welcome!{{msg}}</h1>
      </div>
    </template>
    <script>
    export default {
      data() {
        return {
          msg: this.$route.query.data
        };
      },
    //   data() { //这种方式也可以
    //     return {
    //       msg: null
    //     };
    //   },
      // created() {
      //   this.msg = this.$route.query.data;
      // }
    }
    </script>

    3.error.vue

    <template>
      <div>
        <h1>登录错误:{{msg}}</h1>
      </div>
    </template>
    <script>
    export default {
      // data() {
      //   return {
      //     msg: this.$route.query.data
      //   };
      // }, //使用这种方式也可以显示 msg
      data() {
        return {
          msg: null
        };
      },
      created() {
        this.msg = this.$route.query.message;
      }
    };
    </script>

    五、路由

    页面写好了,我们需要依次显示这三个页面,这里我们统一使用路由来管理显示页面,路由的官方文档见:vue 路由

    本着先实践,后理解的码农学习方式。我们先使用路由显示三个页面后,再去理解Vue路由这个功能点。

    1.创建路由配置文件

    在刚才建立的 router 文件夹下创建一个 index.js 文件,内容如下

    import Vue from 'vue' //引入 Vue
    import VueRouter from 'vue-router' //引入 Vue 路由
    
    Vue.use(VueRouter); //安装插件
    
    export const constantRouterMap = [
        //配置默认的路径,默认显示登录页
        { path: '/', component: () => import('@/views/login')},
    
        //配置登录成功页面,使用时需要使用 path 路径来实现跳转
        { path: '/success', component: () => import('@/views/success')},
    
        //配置登录失败页面,使用时需要使用 path 路径来实现跳转
        { path: '/error', component: () => import('@/views/error'), hidden: true }
    ]
    
    export default new VueRouter({
        // mode: 'history', //后端支持可开
        scrollBehavior: () => ({ y: 0 }),
        routes: constantRouterMap //指定路由列表
    })

    2.将路由添加到程序入口

    路由配置文件写好,我们需要把他引入到 main.js 中,在项目的 src 目录根节点下,找到 main.js,添加内容如下:

    import Vue from 'vue'
    import App from './App.vue'
    import './plugins/element.js'
    import router from './router' //引入路由配置
    
    Vue.config.productionTip = false
    
    new Vue({
      render: h => h(App),
      router, //使用路由配置
    }).$mount('#app')
    

    3.配置路由的出入口

    现在路由已经完全引入到项目了,但是路由还需要一个出入口,这个出入口用来告诉路由将路由的内容显示在这里。上面 main.js 配置的第一个 vue 显示页面为 App.vue ,因此我们修改 App.vue 内容如下

    <template>
      <div id="app">
        <!-- 路由的出入口,路由的内容将被显示在这里 -->
        <router-view/>
      </div>
    </template>
    
    <script>
      export default {
        name: 'App'
      }
    </script>

    <router-view/> 就是显示路由的出入口。

    现在保存 App.vue 文件后,当前项目会被重新装载运行,在刚才浏览的界面就会看到登录界面如下:

    4.路由跳转

    在 login.vue 中可以使用 this.$router.push({path: "路径"}) 来跳转到指定路径的路由组件中,下面是通过路由跳转到 error.vue 与 success.vue的代码

    this.$router.push({path: "/success"}); //跳转到成功页
    或
    this.$router.push({path: "/error"}); //跳转到失败页

    六、使用 Vuex + Axios 方式进行网络请求

    1.Axios

    axios 是一个网络请求构架,官方推荐使用这种方式进行 http 的请求。

    1) 在 utils 包下封装一个请求工具类 request.js

    import axios from 'axios' //引入 axios
    import baseUrl from '../api/baseUrl' //使用环境变量 + 模式的方式定义基础URL
    
    // 创建 axios 实例
    const service = axios.create({
      baseURL: baseUrl, // api 的 base_url
      timeout: 15000, // 请求超时时间
    })
    
    export default service

    这里的 baseUrl 涉及 Vue CLI3 的环境变量与模式的概念,见:Vue 环境变量和模式(设置通用baseUrl)

    2) 登录请求接口 API

    在 api 文件夹下,创建一个登录API文件:login.js

    import request from '@/utils/request' //引入封装好的 axios 请求
    
    export function login(username, password) { //登录接口
      return request({ //使用封装好的 axios 进行网络请求
        url: '/admin/login',
        method: 'post',
        data: { //提交的数据
          username,
          password
        }
      })
    }

    2.使用 Vuex 封装 axios

    Vuex 是一个状态管理构架,官方文档:Vuex

    1)封装 Vuex 中的 module

    在 store 文件夹下创建一个 modules 文件夹,然后在此文件夹下创建一个 user.js 文件

    import { login } from '@/api/login'//引入登录 api 接口
    
    const user = {
      actions: {
        // 登录
        Login({ commit }, userInfo) { //定义 Login 方法,在组件中使用 this.$store.dispatch("Login") 调用
          const username = userInfo.username.trim()
          return new Promise((resolve, reject) => { //封装一个 Promise
            login(username, userInfo.password).then(response => { //使用 login 接口进行网络请求
              commit('') //提交一个 mutation,通知状态改变
              resolve(response) //将结果封装进 Promise
            }).catch(error => {
              reject(error)
            })
          })
        },
      }
    }
    export default user

    这里的代码值得解释一下:官方文档对应:Vuex actions

    1.首先引入 login 接口,之后使用登录接口进行网络请求。

    2.定义一个 名为 Login 的 action 方法,Vue 组件通过 this.$store.dispatch("Login") 调用

    3.Promise,这个类很有意思,官方的解释是“store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch 仍旧返回 Promise”。这话的意思组件中的 dispatch 返回的仍是一个 Promise 类,因此推测 Promise 中的两个方法 resolve() 与 reject() 分别对应 dispatch 中的 then 与 catch。

    2)创建 Vuex

    在 store 文件夹下创建一个 index.js 文件

    import Vue from 'vue' //引入 Vue
    import Vuex from 'vuex' //引入 Vuex
    import user from './modules/user' //引入 user module
    
    Vue.use(Vuex)
    
    const store = new Vuex.Store({
      modules: {
        user //使用 user.js 中的 action
      }
    })
    
    export default store

    3) 将 Vuex 添加到 main.js 文件

    修改之前的 main.js 文件如下:

    import Vue from 'vue'
    import App from './App.vue'
    import './plugins/element.js'
    import router from './router' //引入路由配置
    import store from './store' //引入 Vuex 状态管理
    
    Vue.config.productionTip = false
    
    new Vue({
      render: h => h(App),
      router, //使用路由配置
      store //使用 Vuex 进行状态管理
    }).$mount('#app')

    重新运行项目,在 Chrome 浏览器中进入调试模式,点击登录按钮

    可以看到有发送一个 8088 端口的请求,至此 Vue 端的所有代码已经完成。

    -------------------------------Springboot 开发-------------------------------

    项目创建就不提了,网上有很多,只要使用 Spring Assistant 创建就好。

    整体目录结构如下

    1.在 application.yml 修改端口号

    不要和 Vue 在一个 8080 端口上:

    server:
      port: 8088

    2.解决跨域问题

    这里有一个跨域问题,即 Vue 使用 8080 端口,要访问 8088 端口的服务器,会报错。错误信息如下:

    Access to XMLHttpRequest at 'http://localhost:8088/admin/login' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

    这个问题在 Vue 端或在 Springboot 端处理都可以,我在 Springboot 端处理的,写一个 CorsConfig 类内容如下,不要忘了 @Configuration 注解。

    @Configuration
    public class CorsConfig {
        private CorsConfiguration buildConfig() {
            CorsConfiguration corsConfiguration = new CorsConfiguration();
            corsConfiguration.addAllowedOrigin("*"); // 1
            corsConfiguration.addAllowedHeader("*"); // 2
            corsConfiguration.addAllowedMethod("*"); // 3
            return corsConfiguration;
        }
    
        @Bean
        public CorsFilter corsFilter() {
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            source.registerCorsConfiguration("/**", buildConfig()); // 4
            return new CorsFilter(source);
        }
    }

    3.IErrorCode 接口

    Java 版本

    public interface IErrorCode {
        long getCode();
        String getMessage();
    }

    Kotlin 版本

    interface IErrorCode {
        fun getCode(): Long
        fun getMessage(): String
    }

    4.CommonResult 类

    Java 版本

    public class CommonResult<T> {
        private long code;
        private String message;
        private T data;
    
        protected CommonResult() {
        }
    
        protected CommonResult(long code, String message, T data) {
            this.code = code;
            this.message = message;
            this.data = data;
        }
    
        /**
         * 成功返回结果
         *
         * @param data 获取的数据
         */
        public static <T> CommonResult<T> success(T data) {
            return new CommonResult<T>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
        }
    
        /**
         * 成功返回结果
         *
         * @param data    获取的数据
         * @param message 提示信息
         */
        public static <T> CommonResult<T> success(T data, String message) {
            return new CommonResult<T>(ResultCode.SUCCESS.getCode(), message, data);
        }
    
        /**
         * 失败返回结果
         *
         * @param errorCode 错误码
         */
        public static <T> CommonResult<T> failed(IErrorCode errorCode) {
            return new CommonResult<T>(errorCode.getCode(), errorCode.getMessage(), null);
        }
    
        /**
         * 失败返回结果
         *
         * @param message 提示信息
         */
        public static <T> CommonResult<T> failed(String message) {
            return new CommonResult<T>(ResultCode.FAILED.getCode(), message, null);
        }
    
        /**
         * 失败返回结果
         */
        public static <T> CommonResult<T> failed() {
            return failed(ResultCode.FAILED);
        }
    
        /**
         * 参数验证失败返回结果
         */
        public static <T> CommonResult<T> validateFailed() {
            return failed(ResultCode.VALIDATE_FAILED);
        }
    
        /**
         * 参数验证失败返回结果
         *
         * @param message 提示信息
         */
        public static <T> CommonResult<T> validateFailed(String message) {
            return new CommonResult<T>(ResultCode.VALIDATE_FAILED.getCode(), message, null);
        }
    
        /**
         * 未登录返回结果
         */
        public static <T> CommonResult<T> unauthorized(T data) {
            return new CommonResult<T>(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data);
        }
    
        /**
         * 未授权返回结果
         */
        public static <T> CommonResult<T> forbidden(T data) {
            return new CommonResult<T>(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data);
        }
    
        public long getCode() {
            return code;
        }
    
        public void setCode(long code) {
            this.code = code;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
        public T getData() {
            return data;
        }
    
        public void setData(T data) {
            this.data = data;
        }
    }

    Kotlin 版本

    class CommonResult<T> {
        var code: Long = 0
        var message: String? = null
        var data: T? = null
    
        constructor(code: Long, message: String, data: T?) {
            this.code = code
            this.message = message
            this.data = data
        }
    
        companion object {
    
            /**
             * 成功返回结果
             * @param data 获取的数据
             */
            fun <T> success(data: T): CommonResult<T> {
                return CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data)
            }
    
            /**
             * 成功返回结果
             * @param data 获取的数据
             * @param  message 提示信息
             */
            fun <T> success(data: T, message: String): CommonResult<T> {
                return CommonResult(ResultCode.SUCCESS.getCode(), message, data)
            }
    
            /**
             * 失败返回结果
             * @param errorCode 错误码
             */
            fun <T> failed(errorCode: IErrorCode): CommonResult<T> {
                return CommonResult<T>(errorCode.getCode(), errorCode.getMessage(), null)
            }
    
            /**
             * 失败返回结果
             * @param message 提示信息
             */
            fun <T> failed(message: String): CommonResult<T> {
                return CommonResult<T>(ResultCode.FAILED.getCode(), message, null)
            }
    
            /**
             * 失败返回结果
             */
            fun failed(): CommonResult<Any> {
                return failed(ResultCode.FAILED)
            }
    
            /**
             * 参数验证失败返回结果
             */
            fun validateFailed(): CommonResult<Any> {
                return failed(ResultCode.VALIDATE_FAILED)
            }
    
            /**
             * 参数验证失败返回结果
             * @param message 提示信息
             */
            fun <T> validateFailed(message: String): CommonResult<T> {
                return CommonResult<T>(ResultCode.VALIDATE_FAILED.getCode(), message, null)
            }
    
            /**
             * 未登录返回结果
             */
            fun <T> unauthorized(data: T): CommonResult<T> {
                return CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data)
            }
    
            /**
             * 未授权返回结果
             */
            fun <T> forbidden(data: T): CommonResult<T> {
                return CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data)
            }
        }
    }

    5.ResultCode 枚举

    Java 版本

    public enum ResultCode implements IErrorCode {
        SUCCESS(200, "操作成功"),
        FAILED(500, "操作失败"),
        VALIDATE_FAILED(404, "参数检验失败"),
        UNAUTHORIZED(401, "暂未登录或token已经过期"),
        FORBIDDEN(403, "没有相关权限");
        private long code;
        private String message;
    
        private ResultCode(long code, String message) {
            this.code = code;
            this.message = message;
        }
    
        public long getCode() {
            return code;
        }
    
        public String getMessage() {
            return message;
        }
    }

    Kotlin 版本

    enum class ResultCode(private val code: Long, private val message: String) : IErrorCode {
        SUCCESS(200, "操作成功"),
        FAILED(500, "操作失败"),
        VALIDATE_FAILED(404, "参数检验失败"),
        UNAUTHORIZED(401, "暂未登录或token已经过期"),
        FORBIDDEN(403, "没有相关权限");
    
        override fun getCode(): Long {
            return code
        }
    
        override fun getMessage(): String {
            return message
        }
    }

    6.User类

    Java 版本

    public class User {
    
        private int id;
        private String username;
        private String password;
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    }

    Kotlin 版本

    data class User(
            val id: Int,
            val username: String,
            val password: String)

    7.LoginController 类

    Java 版本

    @RestController
    public class LoginController {
    
        @RequestMapping(value = "/admin/login", method = RequestMethod.POST)
        public CommonResult login(@RequestBody User user) {
            if (user.getUsername().equals("admin") && user.getPassword().equals("123456"))
                return CommonResult.success("admin");
            else
                return CommonResult.validateFailed();
        }
    }

    Kotlin 版本

    @RestController //此注解是 @ResponseBody 和 @Controller 的组合注解,可返回一个 JSON
    class LoginController {
    
        @RequestMapping(value = ["/admin/login"], method = [RequestMethod.POST])
        fun admin(@RequestBody user: User): CommonResult<*> {
            return if (user.username == "admin" && user.password == "123456") {
                CommonResult.success("admin")
            } else {
                CommonResult.validateFailed()
            }
        }
    }

    启动两端程序

    输入正确的账号密码

    输入错误的账号密码

    七、GitHub源码地址

    vue端:https://github.com/xiaojinlai/vue-spring-login-summed

    Java端:https://github.com/xiaojinlai/vue-login-java

    Java端 - Kotlin版本:https://github.com/xiaojinlai/vue-login-kotlin

    注:Kotlin 版本只是我本人用习惯了 Kotlin,就功能而言与Java是一样的。大家如果不喜欢可以不用理会,如果有感兴趣的可以看看,Kotlin 是 Google 推出的一种简洁性语言,主推在 Android 上,用习惯后还是蛮喜欢的。学习起来也不难,内容也不多,推荐一个学习 Kotlin 的网址:https://www.kotlincn.net/docs/reference/

    展开全文
  • SpringBoot vue

    2017-12-21 11:13:00
    springboot 整合vue就行前后端完全分离,监听器,过滤器,拦截器 https://github.com/ninuxGithub/spring-boot-vue-separateA blog built up with Spring Boot in the back end and Vue.js in the front-end...



    springboot 整合vue就行前后端完全分离,监听器,过滤器,拦截器 https://github.com/ninuxGithub/spring-boot-vue-separate
    A blog built up with Spring Boot in the back end and Vue.js in the front-end  https://github.com/arocketman/SpringBlog  (thymeleaf vue ,没有前后端分离)

    overview

    Now about Web develop fields. It's very bloated, outmoded and some development efficiency have a lower with each other than other dynamic language when people refers to Java. Even before somebody shouts loudly ‘Java was died’. But is this really the case? In fact, If you often attention to Java in long time, your feel is too deep. Though it's many disadvantages and verbose. It couldn't be denied that Java is still best language in industry member, and advance with the times. This project is a CRUD demo example base Spring Boot with Vue2 + webpack2. I hope pass thought this project for express Java microservice fast full stack base web practice.

    Why Spring Boot

    Spring is a very popular Java-based framework for building web and enterprise applications. Unlike many other frameworks, which focus on only one area, Spring framework provides a wide verity of features addressing the modern business needs via its portfolio projects.

    In relation to Spring, Spring Boot aims to make it easy to create Spring-powered, production-grade applications and services with minimum fuss. It takes an opinionated view of the Spring platform so that new and existing users can quickly get to the bits they need.

    The diagram below shows Spring Boot as a point of focus on the larger Spring ecosystem:

    spring_vue

    The primary goals of Spring Boot are:

    • To provide a radically faster and widely accessible ‘getting started’ experience for all Spring development.

    • To be opinionated out of the box, but get out of the way quickly as requirements start to diverge from the defaults.

    • To provide a range of non-functional features that are common to large classes of projects (e.g. embedded servers, security, metrics, health checks, externalized configuration).

    Spring Boot does not generate code and there is absolutely no requirement for XML configuration.

    Below are this project code snippet. Do you think simple?

    @RestController
    @RequestMapping("/api/persons")
    public class MainController {
    
        @RequestMapping(
                value = "/detail/{id}", 
                method = RequestMethod.GET, 
                produces = MediaType.APPLICATION_JSON_VALUE
                )
        public ResponseEntity<Persons> getUserDetail(@PathVariable Long id) {
    
            /*
            *    @api {GET} /api/persons/detail/:id  details info
            *    @apiName GetPersonDetails
            *    @apiGroup Info Manage
            *    @apiVersion 1.0.0
            *
            *    @apiExample {httpie} Example usage:
            *
            *        http GET http://127.0.0.1:8000/api/persons/detail/1
            *
            *    @apiSuccess {String} email
            *    @apiSuccess {String} id
            *    @apiSuccess {String} phone
            *    @apiSuccess {String} sex
            *    @apiSuccess {String} username
            *    @apiSuccess {String} zone
            */
    
            Persons user = personsRepository.findById(id);
    
            return new ResponseEntity<>(user, HttpStatus.OK);
        }
    
    }

    Why MVVM

    Although it seems similar to MVC (except with a "view model" object in place of the controller), there's one major difference — the view owns the view model. Unlike a controller, a view model has no knowledge of the specific view that's using it.

    This seemingly minor change offers huge benefits:

    1. View models are testable. Since they don't need a view to do their work, presentation behavior can be tested without any UI automation or stubbing.

    2. View models can be used like models. If desired, view models can be copied or serialized just like a domain model. This can be used to quickly implement UI restoration and similar behaviors.

    3. View models are (mostly) platform-agnostic. Since the actual UI code lives in the view, well-designed view models can be used on the iPhone, iPad, and Mac, with only minor tweaking for each platform.

    4. Views and view controllers are simpler. Once the important logic is moved elsewhere, views and VCs become dumb UI objects. This makes them easier to understand and redesign. In short, replacing MVC with MVVM can lead to more versatile and rigorous UI code.

    In short, replacing MVC with MVVM can lead to more versatile and rigorous UI code.

    Why to choose Vue.js

    Vue.js is relatively new and is gaining lot of traction among the community of developers. VueJs works with MVVM design paradigm and has a very simple API. Vue is inspired by AngularJS, ReactiveJs and updates model and view via two way data binding.

    Components are one of the most powerful features of Vue. They help you extend basic HTML elements to encapsulate reusable code. At a high level, components are custom elements that Vue’s compiler attaches behavior to.

    spring_vue

    What's Webpack

    Webpack is a powerful tool that bundles your app source code efficiently and loads that code from a server into a browser. It‘s excellent solution in frontend automation project.

    Demo

    This's a sample ShangHai people information system as example demo.

    demo-image

    Feature (v0.1)

    • Spring Boot (Back-end)

      • Build RestFul-API on SpringBoot with @RequestMapping and base CRUD logic implementation

      • Handle CORS(Cross-origin resource sharing)

      • Unit test on SpringBoot

      • Support hot reload

      • Add interface documents about it's rest-api

      • Pagination implementation of RestFul-API with JPA and SpringBoot

    • VueJS & webpack (front-end)

      • Follow ECMAScript 6

      • What about coding by single file components in vueJS

      • Simple none parent-child communication and parent-child communication

      • Interworking is between data and back-end

      • How grace import third JS package in vue

      • Handle format datetime

      • Pagination implementation

      • Reusable components

        • DbHeader.vue
        • DbFooter.vue (sticky footer)
        • DbFilterinput.vue
        • DbModal.vue
        • DbSidebar.vue
        • DbTable.vue
      • Config front-end env on webpack2 (include in vue2, handle static file, build different environment...... with webpack2)

    Main technology stack

    • Java 1.7
    • Spring Boot 1.5.x
    • Maven
    • sqlite (not recommend, only convenience example)
    • vueJS 2.x
    • webpack 2.x
    • element ui
    • axios

    Preparation

    • Please must install Java 1.7 or even higher version

    • install Node.js / NPM

    • Clone Repository

        git clone https://github.com/boylegu/SpringBoot-vue.git
        
        cd springboot_vue
      

    Installation

    • Build front-end environment

        cd springboot_vue/frontend
      
        npm install 
      

    Usage

    • Run back-end server

        cd springboot_vue/target/
        
        java -jar springboot_vue-0.0.1-SNAPSHOT.jar
      

    • Run Front-end Web Page

        cd springboot_vue/frontend
      
        npm run dev
      

    You can also run cd springboot_vue/frontend;npm run build and it's with Nginx in the production environment

    Future Plan

    This project can be reference,study or teaching demonstration. After, I will update at every increme version in succession. In future,I have already some plan to below:

    1. User Authentication
    2. state manage with vuex
    3. use vue-route
    4. add docker deploy method
    5. support yarn ... ...

    Support

    1. Github Issue

    2. To e-mail: gubaoer@hotmail.com

    3. You can also join to QQ Group: 315308272

    Related projects

    SpringBoot + 前端MVVM 基于Java的微服务全栈快速开发实践

    spring_vue

    Convenient & efficient and better performance for Java microservice full stack.

    Commemorate the 6 anniversary of enter the profession.

    Give beginner as a present.

    ———————By Boyle Gu

    背景

    如今Web开发领域,当有人提到Java时,总会让人觉得臃肿、古老而过时且开发效率没有某些动态语言高效,甚至在此之前还有人高喊“Java 已死!”,但是事实真是如此吗?其实如果你一直关注着Java,那你的感悟会更深,尽管它有很多的缺点和啰嗦,但不可否认,Java依然是工业界中最优秀的语言,而且它一直保持着与时俱进。本项目将使用SpringBoot + Vue2 + Webpack2 配合最简单CRUD的逻辑来展示一个基于Java的微服务全栈快速开发实践的Demo。

    在某些时候,其开发效率已经并不比某些动态语言低。

    为什么是SpringBoot

    首先先来简单的介绍一下Spring,它是目前Java生态中最广为人知、流行的企业级Web框架。不像其他一些框架仅聚焦在某个领域,Spring框架通过其容器化组件式管理及开发,可提供或定制各式各样的功能来满足企业化需求。

    那么相较于Spring,Spring Boot的目标是更加容易的创建Spring应用、建立自动化、最少人为干预的生产级配置,真正意义做到开箱即用,并且对于新用户及Spring平台的用户极易上手,快速开发。

    下图主要展示了Spring Boot在Spring庞大的生态圈中的层级关系

    spring_vue

    SpringBoot的目标主要:

    • 为所有Spring开发提供一个从根本上更快,且随处可得的入门体验。

    • 开箱即用,但通过不采用默认设置可以快速摆脱这种方式。

    • 提供一系列大型项目常用的非功能性特征,比如:内嵌服务器,安全,指标,健康检测,外部化配置。

    绝对没有代码生成,也不需要XML配置。

    下面展示的是本项目的SpringBoot相关代码片段,你觉得简单么?

    @RestController
    @RequestMapping("/api/persons")
    public class MainController {
    
        @RequestMapping(
                value = "/detail/{id}", 
                method = RequestMethod.GET, 
                produces = MediaType.APPLICATION_JSON_VALUE
                )
        public ResponseEntity<Persons> getUserDetail(@PathVariable Long id) {
    
            /*
            *    @api {GET} /api/persons/detail/:id  details info
            *    @apiName GetPersonDetails
            *    @apiGroup Info Manage
            *    @apiVersion 1.0.0
            *
            *    @apiExample {httpie} Example usage:
            *
            *        http GET http://127.0.0.1:8000/api/persons/detail/1
            *
            *    @apiSuccess {String} email
            *    @apiSuccess {String} id
            *    @apiSuccess {String} phone
            *    @apiSuccess {String} sex
            *    @apiSuccess {String} username
            *    @apiSuccess {String} zone
            */
    
            Persons user = personsRepository.findById(id);
    
            return new ResponseEntity<>(user, HttpStatus.OK);
        }
    
    }

    为什么是MVVM

    那么在我继续之前,我也想和大家回顾一下Web开发的发展简史:

    • 第一阶段: 网页三剑客,生猛的通过原生javascript直接操作Dom树;

    • 第二阶段: JQuery诞生,配合前端MVC为代表的Backbone.js, 让我们可以优雅而简单的操作Dom树;

    • 第三阶段: 后端架构升级为MVC,前后端分工更清晰,前端工程化、ECMAScript规范开始崭露头角;

    • 第四阶段: 后端架构进入了微服务时代,前端架构不仅升级为MVVM,ES6更是成为目前事实上的标准;

    在这里,我不想过于神化MVVM有多么的先进,JQuery为代表的MVC有多么的落后,但确实MVVM有着很多先进的特性:

    • 低开销

    • 易维护

    • 可重用

    为什么选择Vue.js

    Vue.js是MVVM设计模式中目前最火热的一个前端框架之一,除了性能表现优异之外,与类似React相比,更轻量级、更容易上手。

    通过Vue中的“单文件组件”特性,更灵活的定义组件,不仅使代码结构更清晰,而且能与任何其他组件进行随意组合,更具复用性。

    sanic_vue

    Webpack是什么

    Webpack提供了一整套前端工程自动化的解决方案

    Demo

    一个简单的“上海人员信息查询系统”作为例子

    demo-image

    具备的功能(v0.1)

    • Spring Boot (后端)

      • 通过在Spring Boot中建立基于RestFul-API并使用@ RequestMapping实现一个基本的CRUD逻辑

      • 处理CORS(跨域资源共享)

      • 在Spring Boot中进行单元测试

      • 支持热加载

      • 增加api接口文档

      • 通过SpringBoot配合JPA来实现RestFul-API的分页

    • VueJS & webpack (前端)

      • 遵循ECMAScript 6 规范

      • 如何在vue中使用‘单文件组件’进行开发编码

      • 演示‘非父子组件’如何进行简单的通信以及‘父子组件’之间如何传递数据

      • 如何和后端进行数据交互

      • 如何在vue中优雅的引入第三方JS库

      • 格式化时间

      • 分页实现

      • 可复用组件

        • DbHeader.vue
        • DbFooter.vue (sticky footer)
        • DbFilterinput.vue
        • DbModal.vue
        • DbSidebar.vue
        • DbTable.vue

        得益于类似vue、react等MVVM模式,本项目的任何组件,只要您觉得合适,都可以复用在您的任何项目中,避免重复造轮子。

      • 如何通过webpack2配置来自动化构建前端环境(包括如何配置vue2、处理静态文件,构建不同环境等等)

    本项目主要技术栈

    • Java 1.7
    • Spring Boot 1.5.x
    • Maven
    • sqlite (not recommend, only convenience example)
    • vueJS 2.x
    • webpack 2.x
    • element ui
    • axios

    准备工作

    • 安装JDK1.7或更新的版本

    • 安装Node.js/NPM

    • 克隆仓库

        git clone https://github.com/boylegu/SpringBoot-vue.git
        
        cd springboot_vue
      

    安装

    • 编译前端开发环境

        cd springboot_vue/frontend
      
        npm install 
      

    使用

    • 运行Spring Boot后端服务

        cd springboot_vue/target/
      
        java -jar springboot_vue-0.0.1-SNAPSHOT.jar
      

    • 运行前端服务

        cd springboot_vue/frontend
      
        npm run dev
      

    你也可以在生产环境中运行cd springboot_vue/frontend;npm run build进行编译并配合Nginx

    未来计划

    本项目可以作为工作参考、学习或者教学演示,之后将陆续以版本的形式,即每个版本都会新增不同的功能演示项,不定期进行发布更新,有以下功能已经在计划之中:

    1. 用户认证
    2. 引入更高级的vuex组件通信机制
    3. 演示vue-route的使用
    4. 加入docker部署环境
    5. 新增针对yarn的支持 ... ...

    技术、教学支持

    由于个人时间暂时有限,关于Spring、Vue、webpack等所有的核心的议题内容非常庞大,因此我将以以下形式来回答和解释关于本项目Demo问题:

    1. 以Github Issue的形式进行提问
    2. 电子邮件的形式 gubaoer@hotmail.com
    3. QQ群:315308272

    相关项目

    https://github.com/boylegu/SpringBoot-vue/blob/master/README-CN.md

     

    展开全文
  • 招聘网 springboot vue

    2019-03-28 20:18:40
    招聘网 springboot vue
  • springboot vue2项目

    2018-08-07 21:55:24
    springboot vue2项目
  • 主要介绍了springboot vue 跨域问题的解决,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 主要介绍了Springboot vue导出功能实现代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • vhr,项目名称:Springboot Vue。.zip
  • springboot vue.js结合

    千次阅读 2017-12-06 10:19:05
    springboot vue.js 结合 折腾过几个项目用springboot后台,vue.js前台的技术架构,存在一定时间间隔,有些东西总是需要重新去学习,记录下来,并在github上维护一个示例SpringBootVue,后续的版本fork该项目进行...

    springboot vue.js 结合

    折腾过几个项目用springboot后台,vue.js前台的技术架构,存在一定时间间隔,有些东西总是需要重新去学习,记录下来,并在github上维护一个示例SpringBootVue,后续的版本fork该项目进行扩展即可。

    参考链接:
    1、springboot官网
    2、maven-resources-plugin插件
    3、frontend-maven-plugin插件
    4、饿了么前端框架Element
    5、spring-boot convert-jar-to-war
    6、github 大神开源的Ele实现
    7、github 大神开源的Ele后台实现
    8、vue2.0构建单页应用最佳实战
    项目结构:
    SpringBootVue
    - frontend
    - backend

    展开全文
  • springboot + vue
  • springboot vue整合及安全

    由于我们之前的springboot+template html版本在H5方向开发成本和入手成本比较高,以企业微信为入口的H5不断有新需求涌入,原来的粗犷式java web实在是效率低,效果也不好。我这个javaer决定研究下当下流行的前端解决方案。在一番搜索之后,发下当前前端的解决方案(非APP方向)基本以PC(VUE+ElementUI)和H5(Vue+)为主。

    PC部分暂时由于内网PC使用,升级需求不迫切,所以我主要锁定H5(Vue+Vant)。

    在学习了一个月的Vue全家桶和Vant集成后,我们原来的H5(功能比较简单),已经完全在我的电脑上模拟完成了。剩下的就是和后台整合的问题。

    搜索网上解决方案后,个人心里大概有所思路,着手搭建整合。

    1、Vue项目中配置History路由模式。

    2、临时新建springboot项目,将Vue打包后的文件放入static目录下,修改index.html中的js,css引用路径。

    3、Controller中模拟简单的登录拦截和请求。

    启动工程,访问首页,没有问题,点点其他,ok。好多网上文章到此就结束了,可是,正常的网页程序必须考虑登录认证、权限控制、数据控制、渗透安全。随手在浏览器地址栏输入一个不存在的地址,404;手动输入一个存在的vue路径,404。。?what?发生了神马?打开浏览器debug,查看手工输入url和鼠标点击vue页面的区别,得出结论:Vue是本地路由,浏览器url是网路请求。也许我对Vue不熟悉,造成笑话,但是,对于这个结果,我却得到一个结论,Vue和Springboot整合的项目不同于原来的Web应用,不能进行Vue路由的安全控制,因为路由根本不和后台交互。

    这时候肯定有人diss我,Vue router可以实现,可是,真正的安全控制,一定是后台实现,而不是前台实现。为什么呢?因为,如果是普通用户,一般情况不涉及安全问题;如果是恶意者,那么前台本地的filter,就是一段代码能解决的问题。所以,Vue Router只能解决一些常见的本地filter问题,对于认证鉴权,渗透注入之类的得靠后台。当然,vue router也可以减轻url鉴权得工作,因为有一部分页面不用再通过后台filter了。

    那么问题来了,如何处理Vue和后台的关系,这便成为了整合的具体方案关键。最起码,原始Web的url访问控制不能对本地路由进行过滤,所以,我们只能从其他方面入手,换句话说,就是只能从数据层面解决控制问题。那么我们就按方案来说:

    1、springboot web模式。

    具体做法就是上文说到的,将Vue打包后的dist放入springboot static目录,启动springboot作为服务端口,页面和api都由springboot管理。

    2、前后端分离模式。

    搭建独立的web服务,使用静态资源代理vue,由于跨域的问题,所以,需要在vue端口同时代理api服务。例如,拿Nginx代理vue静态资源为根路径80,代理其他服务为/api/xxx。这样,js请求就不发生跨域问题。

    以上,就是两种常见的整合方案。接下来就是安全问题。

    1、认证。

    由于没有原来的login页面登录,所以需要后端提供api登录的方式。一般使用token认证之类的。当然,也有和我们一样使用企业微信(微信)登录的,这种的,可以先使用正常的非vue登录流程,认证成功后,return或redirect到vue的首页。

    未认证的情况:方案一,vue首页url是通过controller控制的,所以不认证无法访问首页。方案二,由于静态vue的url的不可拦截性,用户可以直接访问首页,只能通过认证后的数据来鉴权控制。

    2、鉴权。

    上文说到,由于url不可拦截,所以,我们必须从页面组件到数据,涉及到用户权限的部分,必须从后台获取。举个例子,首页有5个模块,1-2属于角色A,3属于角色B,,4-5属于角色C。那如果Vue层直接全部绘制的话,不同角色登录首页都可看见,即使你再拦截一次,那用户也可以通过你的模块去猜测可能的路径。所以,尽量加载时,根据用户角色动态的绘制router或Nav。另外,对于后台请求,也必须携带认证后的token等信息进行接口访问。

    对于springboot层,则需要配置filter进行认证层的验证。对于不同角色请求,从认证池中校验该角色是否有接口的请求权限。对于页面模块和菜单,需要通过模块配置表和角色表的关联关系,实现接口查询用户的模块和菜单。

    3、数据权限。

    认证完成后,后台记录用户的token或者session。下次请求通过token或session中的用户信息,注入到接口,api服务所有的数据操作必须携带用户信息进行数据筛选。防止接口篡改出来的用户信息。

    4、报文安全。

    统一由web api层的filter实现报文的过滤或验签解密。可以集成到api服务,或者通过网关实现(见我的另一篇文章)。这里啰嗦一下实现问题,因为request body的不可复制,所以springboot后台拦截请求必须重写request,然后去获取报文,执行验签、解密、然后需要检查XSS,SQL等攻击问题。

     

    以上,就是我自己整合中的一点过程和思考,没有直接贴代码,因为涉及到的东西太多。Vue部分也是临时学习,不敢卖弄。springboot部分的,有时间把用户体系和安全部分写点文章描述一下。

    展开全文
  • Springboot Vue验证码校验

    千次阅读 2020-05-01 17:34:02
    项目中在登录注册环节肯定会用到验证码校验,Springboot整合Thymeleaf验证码校验之前已经做过了,那么现在SpringbootVue前后端分离项目中该如何使用呢。 实现思路: 后端利用工具类生成验证码code,将验证码code...
  • springboot vue组件写的个人博客系统

    千次阅读 2018-02-07 15:28:00
    springboot vue组件写的个人博客系统 个人写的博客管理系统,学习java不到一年 欢迎探讨交流学习 https://github.com/Arsense/ssmBlog 项目地址 如果觉得好的 帮忙star一下 谢谢! 基本技术 环境: ...
  • springboot vue 登陆发送请求(跨域问题)02 2017年09月28日 22:17:...现在用vuespringboot都有新的方式  第一种  vue2 本身也支持跨域,开发过程中支持这个,方便  第二种  springboot 简单一个类就可以支持...
  • 学生信息管理系统零、基础项目为前后分离项目1、前端(front-end)基于 vue-admin-template 开发2、后端(rear-end)Jdk8Maven3MySQL5.7SpringBoot2SQLYog一、功能模块图图片.png二、系统预览1、登录登录.png2、首页首页...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 11,942
精华内容 4,776
关键字:

springbootvue

vue 订阅
spring 订阅