三天敲一个前后端分离的员工管理系统

码云地址

第一章、项目概述

人事管理系统是每个公司必备的管理系统,可以更方便的管理员工。

需求分析

hr实现对员工的增删改查,管理员实现对hr的更改。对员工进行搜索等功能。

总述

本项目是前后端分离项目,在服务器中运行。前端页面友好。

技术栈选择

  • 前端:vue3、vite2、axios、router、element UI 、nodejs
  • 后端:springboot、springsecurity、springdatajpa、maven、tomcat

环境介绍

  • 前端使用webstorm开发
  • 后端使用idea开发
  • 数据库使用MySQL8.0
  • 可视化工具使用Navicat
  • JDK版本:15
  • maven版本:3.6
  • tomcat版本:9.0
  • npm版本:7.17

效果图展示

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

第二章、设计思路

该系统主要是前端设计页面接受后端的JSON数据,前后端所有数据除了登录页面使用key values 形式,其余全部使用JSON格式。后端使用springdatajpa自动生成的restful风格接口。前端通过axios发起请求获取接口数据。但是在开发中,我们需要克服跨域所带来的的问题。部署阶段就不存在跨域,因为我们会把前端打包到后端,一起部署。这样就不涉及到跨域问题了。当然也可以使用nigx进行代理。

数据库设计

在这里插入图片描述

数据表结构

员工表:

在这里插入图片描述

hr表:

在这里插入图片描述

角色表:

在这里插入图片描述

第三章、功能设计

  • hr登录
  • 权限鉴定
  • 对hr的增删改查
  • 对员工的增删改查

流程图

在这里插入图片描述

第四章、功能实现

后端功能实现

环境准备

在这里插入图片描述

添加springdata rest依赖

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

目录结构

在这里插入图片描述

hr相关功能实现

登录功能

创建vhrdb数据库

在这里插入图片描述

配置jpa连接数据库

#配置数据库
spring.datasource.url=jdbc:mysql://localhost:3306/vhrdb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456

#配置jpa
#1.jpa数据库
spring.jpa.database=mysql
#2.在控制台打印sql
spring.jpa.show-sql=true
#3.jpa数据库平台
spring.jpa.database-platform=mysql
#4.当对象改变更新表
spring.jpa.hibernate.ddl-auto=update
#5.指定方言!!!重要!!!
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
#.....



创建用户相关的表

  • 角色表

    package com.wz.vhrdb.entity;
    import javax.persistence.*;
    
    /**
     * @author: 王泽
     */
    
    @Entity
    @Table(name = "t_role")
    public class Role {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String name;
        private String nameZh;  //角色的中文名
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getNameZh() {
            return nameZh;
        }
    
        public void setNameZh(String nameZh) {
            this.nameZh = nameZh;
        }
    }
    
    
    
  • 用户表

    package com.wz.vhrdb.entity;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.userdetails.UserDetails;
    import javax.persistence.*;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.List;
    
    /**
     * @author: 王泽
     */
    
    @Entity(name = "t_user")
    public class User implements UserDetails {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String username;
        private String password;
        private boolean accountNonExpired;
        private boolean accountNonLocked;
        private boolean credentialsNonExpired;
        private boolean enabled;
        @ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.PERSIST)
        private List<Role> roles;
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public void setAccountNonExpired(boolean accountNonExpired) {
            this.accountNonExpired = accountNonExpired;
        }
    
        public void setAccountNonLocked(boolean accountNonLocked) {
            this.accountNonLocked = accountNonLocked;
        }
    
        public void setCredentialsNonExpired(boolean credentialsNonExpired) {
            this.credentialsNonExpired = credentialsNonExpired;
        }
    
        public void setEnabled(boolean enabled) {
            this.enabled = enabled;
        }
    
        public List<Role> getRoles() {
            return roles;
        }
    
        public void setRoles(List<Role> roles) {
            this.roles = roles;
        }
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            List<SimpleGrantedAuthority> authorities = new ArrayList<>();
            for (Role role : getRoles()) {
                authorities.add(new SimpleGrantedAuthority(role.getName()));
            }
            return authorities;
        }
        @Override
        public String getPassword() {
            return password;
        }
    
        @Override
        public String getUsername() {
            return username;
        }
    
        @Override
        public boolean isAccountNonExpired() {
            return accountNonExpired;
        }
    
        @Override
        public boolean isAccountNonLocked() {
            return accountNonLocked;
        }
    
        @Override
        public boolean isCredentialsNonExpired() {
            return credentialsNonExpired;
        }
    
        @Override
        public boolean isEnabled() {
            return enabled;
        }
    }
    
    
  • 运行程序,创建表。

在这里插入图片描述

创建UserDao接口

package com.wz.vhrdb.dao;

import com.wz.vhrdb.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserDao extends JpaRepository<User,Long> {
    User findUserByUsername(String username);


}

创建UserService类

package com.wz.vhrdb.service;

import com.wz.vhrdb.dao.UserDao;
import com.wz.vhrdb.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

/**
 * @author: 王泽
 */

public class UserService implements UserDetailsService {
    @Autowired
    private UserDao userDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        final User user = userDao.findUserByUsername(username);
        if (user == null){
            throw new UsernameNotFoundException("用户不存在");
        }
        return user;
    }
}

配置springsecurity

package com.wz.vhrdb.config;

import com.wz.vhrdb.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @author: 王泽
 */

public class SecurityConfig extends WebSecurityConfigurerAdapter {
   //密码不加密
    @Bean
    PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    //校验的数据源
    @Autowired
    UserService userService;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService);
    }

    //角色关系
    @Bean
    RoleHierarchy roleHierarchy() {
        RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();
        hierarchy.setHierarchy("ROLE_admin > ROLE_user");
        return hierarchy;
    }

    //放行静态资源
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/js/**","/css/**","/image/**");
    }

    //配置拦截规则和表单配置
    //表单配置待完善
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("admin")//具备某个角色
                .antMatchers("/user/**").hasAnyRole("admin","user")
                .anyRequest().authenticated()//除了上述两个只要登录就能访问
                .and()
                .formLogin()
                .permitAll()
                .and()
                .csrf().disable();

    }
}

注册功能

直接在service中添加增添方法,jpa为我们提供了save

   //用户注册功能(增加用户)
    public User insertUser(User user) {
        return userDao.save(user);
    }
}

在controller中写接口

    /**
     * 新增用户 post /users
     */
    @PostMapping("")
    public User addUser(@RequestBody User user){
        return userService.insertUser(user);
    }

postman测试

{
    "username":"王泽",
    "password":"123",
    "accountNonExpired":true,
    "accountNonLocked":true,
    "credentialsNonExpired":true,
    "enabled":true,
    "roles":[{
        "name":"admin",
        "nameZh":"管理员"
    }]

}

我们可以给实体类设置一些默认值:

private boolean accountNonExpired =true;
    private boolean accountNonLocked = true;
    private boolean credentialsNonExpired =true;
    private boolean enabled =true;

测试:

package com.wz.vhrdb;

import com.wz.vhrdb.dao.UserDao;
import com.wz.vhrdb.entity.Role;
import com.wz.vhrdb.entity.User;
import com.wz.vhrdb.service.UserServiceImpl;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.ArrayList;
import java.util.List;

@SpringBootTest
class VhrdbApplicationTests {

    @Autowired
    UserServiceImpl userService;
    @Test
    void contextLoads() {
        User u1 = new User();
        u1.setUsername("liu");
        u1.setPassword("123");
//        u1.setAccountNonExpired(true);
//        u1.setAccountNonLocked(true);
//        u1.setCredentialsNonExpired(true);
//        u1.setEnabled(true);
        List<Role> rs1 = new ArrayList<>();
        Role r1 = new Role();
        r1.setName("ROLE_admin");
        r1.setNameZh("管理员");
        rs1.add(r1);
        u1.setRoles(rs1);
        userService.insertUser(u1);
        User u2 = new User();
        u2.setUsername("小刘");
        u2.setPassword("123");
//        u2.setAccountNonExpired(true);
//        u2.setAccountNonLocked(true);
//        u2.setCredentialsNonExpired(true);
//        u2.setEnabled(true);
        List<Role> rs2 = new ArrayList<>();
        Role r2 = new Role();
        r2.setName("ROLE_user");
        r2.setNameZh("普通用户");
        rs2.add(r2);
        u2.setRoles(rs2);
        userService.insertUser(u2);
    }
}

在这里插入图片描述

优化登录注册

主要问题有两方面:1.不能重复注册 2.密码加密问题

密码加密问题

首先我们注册的时候要添加密码加密

 //用户注册功能(增加用户)
    public User insertUser(User user) {
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(10);
        user.setPassword(encoder.encode(user.getPassword()));
        return userDao.saveAndFlush(user);
    }

然后我们需要在springsecurity中配置

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
   //密码加密
   @Bean
   PasswordEncoder passwordEncoder() {
       return new BCryptPasswordEncoder(10);
   }

    //校验的数据源
    @Autowired
    UserServiceImpl userService;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
    }
}

检查重复用户

    public User insertUser(User user) {
        User username = userDao.findUserByUsername(user.getUsername());
        if (username == null) {
            BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(10);
            user.setPassword(encoder.encode(user.getPassword()));
            return userDao.save(user);
        }else {
            throw new RuntimeException("用户名已经存在");
        }
    }
}

测试:

我们先用userDao.deleteAll()删除所有记录,然后来测试重复添加

在这里插入图片描述

员工相关功能

我们首先需要一个员工的表(实体类),员工有姓名,性别,年龄,电话,部门,入职时间

创建实体类Personnel

package com.wz.vhrdb.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.util.Date;

/**
 * @author: 王泽
 */
@Entity(name = "t_personnel")
public class Personnel {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String pName;
    private String pSex;
    private Integer pAge;
    private String pClass;
    private String pTel;
    private Date pJoin;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getpName() {
        return pName;
    }

    public void setpName(String pName) {
        this.pName = pName;
    }

    public String getpSex() {
        return pSex;
    }

    public void setpSex(String pSex) {
        this.pSex = pSex;
    }

    public Integer getpAge() {
        return pAge;
    }

    public void setpAge(Integer pAge) {
        this.pAge = pAge;
    }

    public String getpClass() {
        return pClass;
    }

    public void setpClass(String pClass) {
        this.pClass = pClass;
    }

    public String getpTel() {
        return pTel;
    }

    public void setpTel(String pTel) {
        this.pTel = pTel;
    }

    public Date getpJoin() {
        return pJoin;
    }

    public void setpJoin(Date pJoin) {
        this.pJoin = pJoin;
    }
}

在这里插入图片描述

对于员工表我们的需求有,特定的查询,以及增删改查,分页等。

springdatajpa自带的接口基本足够我们本项目的使用

目前所需要的自动生成的接口有:

1.查询所有用户: http://localhost:8989/users  (get)
2.查询所有员工: http://localhost:8989/personnels  (get)
3.增加用户hr: http://localhost:8989/user/adduser (post)
4.增加员工:  http://localhost:8989/personnels  (post)
5.分页查询员工: http://localhost:8989/personnels?page=0&size=5
6.删除hr: http://localhost:8989/users/id   (delete)
7.删除员工: http://localhost:8989/personnels/id (delete)
8.修改员工: http://localhost:8989/personnels/id (put)

优化与前端的交互体验

主要思路:成功与失败都有信息来提示前端

编码:

编写vo包(传值的)中的result类

package com.wz.vhrdb.vo;

/**
 * @author: 王泽
 */

public class Result {
    private Boolean status =true;
    private String msg;

    public Boolean getStatus() {
        return status;
    }

    public void setStatus(Boolean status) {
        this.status = status;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

编写controller

@PostMapping("/adduser")
    public Result addUser(@RequestBody User user){
        Result result =new Result();
        try{
            userService.insertUser(user);
            result.setMsg("hr增加成功");
        }catch (Exception e){
            result.setStatus(false);
            result.setMsg("新增hr失败"+e.getMessage());
        }
        return result;

    }

其余接口均使用springdatajpa自动生成的接口!

前端功能实现

环境搭建

目录结构

在这里插入图片描述

用vite 进行环境搭建

响应尤大的号召,使用vite!

  • npm init @vitejs/app vhrui --template vue

  • cd vhrui

  • npm install

至此项目创建完成!接下来我们需要引入我们所需要的其他组件

引入路由

  • npm install vue-router@next

  • 创建router目录来存放router配置

    import { createRouter,createWebHistory} from "vue-router";
    
    // 路由信息
    const routes = [
        {
            path: "/",
            name: "Index",
            component:  () => import('../views/idnex.vue'),
        },
    ];
    
    // 导出路由
    const router = createRouter({
        history: createWebHistory(),
        routes
    });
    
    export default router;
    
    
  • 在main.js中使用路由

    import { createApp } from 'vue'
    import App from './App.vue'
    import router from "./router/router";
    
    const app = createApp(App)
    app.mount('#app')
    app.use(router)
    
    

注意:在使用的时候要到inde.vue

<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <index></index>
</template>

<script>
import Index from "./views/index.vue";
export default {
  components: {Index}
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

在这里插入图片描述


引入UI组件库
npm install element-plus --save

在main.js中使用

import { createApp } from 'vue'
import ElementPlus from 'element-plus';
import 'element-plus/lib/theme-chalk/index.css';
import App from './App.vue';

const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
引入axios
npm install --save axios vue-axios

在main.js 中使用

import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'

Vue.use(VueAxios, axios)

使用方法:

this.$http.get(api).then((response) => {
  console.log(response.data)
})

开发界面

项目路由:
import { createRouter,createWebHistory} from "vue-router";

// 路由信息
const routes = [
    {
        path: "/",
        name: "index",
        component:  () => import('../components/index.vue'),
    },
    {
        path: "/login",
        name: "login",
        component:  () => import('../components/login.vue'),
    },
    {
        path: "/register",
        name: "register",
        component:  () => import('../views/user/add.vue'),
    },
    {
        path: "/personnel",
        name: "personnel",
        component:  () => import('../components/personnel.vue'),
        children:[
            {
                path: "/users",
                name: "users",
                component:  () => import('../views/user/users.vue'),
            },
            {
                path: "/pindex",
                name: "pindex",
                component:  () => import('../views/personnel/pindex.vue'),
            },
            {
                path: "/class",
                name: "class",
                component:  () => import('../views/personnel/class.vue'),
            },
            {
                path: "/personnels",
                name: "personnels",
                component:  () => import('../views/personnel/personnels.vue'),
            },

        ]
    },


];

// 导出路由
const router = createRouter({
    history: createWebHistory(),
    routes
});

export default router;


组件的开发

这里只写出首页的主要代码

<template>
<div class="bg">
  <div id="b">
  <el-container>
    <el-aside width="50px">
      <div style="height: 800px;">
        <el-steps direction="vertical" >
          <el-step title="后端攻城狮"></el-step>
          <el-step title="前端攻城狮"></el-step>
          <el-step title="全栈工程师" ></el-step>
        </el-steps>
      </div>
    </el-aside>
    <el-container>
      <el-header>
        <el-menu :default-active="activeIndex" class="el-menu-demo" mode="horizontal" @select="handleSelect">
          <el-menu-item index="pindex" style="color:darkorange"><strong>公司主页</strong></el-menu-item>
          <el-menu-item index="personnels" style="color: darkmagenta"><strong>员工管理</strong></el-menu-item>
          <el-menu-item index="class" style="color: darkgreen" disabled><strong>部门管理</strong></el-menu-item>
          <el-menu-item index="users" style="color: darkcyan"><strong>hr管理</strong></el-menu-item>
          <el-menu-item index="/" style="color: red" @click="logout">退出</el-menu-item>
        </el-menu>
      </el-header>
      <el-main>
        <router-view/>
      </el-main>
    </el-container>
  </el-container>
  </div>
</div>
</template>

<script>
export default {
  name: "personnel",
  data() {
    return {
      activeIndex: 'pindex'
    };
  },
  methods: {
    handleSelect(key, keyPath) {
      console.log(key, keyPath);
      this.$router.push(key);
    },
    logout(){
      this.$http.get('http://localhost:3000/logout')
    }
  },
  created() {
    this.$router.push("pindex");
  }
}

</script>

<style scoped>
.bg{
  background-image:url(../assets/image/1.jpg);
  position: absolute;
  top:0;
  left: 0;
  width: 100%;
  height: 100%;
  background-attachment: local;
  background-repeat: no-repeat;
  background-size: cover;
  float: left;
  -o-background-size: cover;
  background-position: center;
}
#b{ background:#000;
  color: #00ff22;
  filter:alpha(Opacity=60);
  -moz-opacity:0.8;opacity: 0.8
}


</style>

第五章、前后端整合所遇到的问题

1.springsecurity+vue如何实现登录验证??

在前后端分离这样的开发架构下,前后端的交互都是通过 JSON 来进行,无论登录成功还是失败,都不会有什么服务端跳转或者客户端跳转之类。

登录成功了,服务端就返回一段登录成功的提示 JSON 给前端,前端收到之后,该跳转该展示,由前端自己决定,就和后端没有关系了。

登录失败了,服务端就返回一段登录失败的提示 JSON 给前端,前端收到之后,该跳转该展示,由前端自己决定,也和后端没有关系了。

successHandler 的功能十分强大,甚至已经囊括了 defaultSuccessUrl 和 successForwardUrl 的功能。

.successHandler((req, resp, authentication) -> {
    Object principal = authentication.getPrincipal();
    resp.setContentType("application/json;charset=utf-8");
    PrintWriter out = resp.getWriter();
    out.write(new ObjectMapper().writeValueAsString(principal));
    out.flush();
    out.close();
})

2.springdatajpa+springsecurity+vue如何实现跨域?

只有开发时存在这种跨域的情况。

解决思路:

  • 部署:前端部署到nigx上,后端通过nigx做请求转发。或者部署到一起
  • 开发环境下:用node.js 做请求转发。

正解:
在这里插入图片描述

https://my.oschina.net/u/1020373/blog/4899705

3.后端传来的json数据如何与前端交互?

前端接收:发送axios异步请求,用tableData接收

//tableData[] 
findAll(){
      this.axios.get('http://localhost:8989/users').then(res=>{
        this.tableData=res.data;
      })
}

// 初始化的时候调用方法
created() {
    this.findAll();
  }

前端发来的post,后端接收

这时候,我们就需要再复习一下后端所用到的一些注解:

@RequestBody :可以将body里面所有的json数据传到后端,后端再进行解析。

@RequestParam :接收的参数是来自requestHeader中,即请求头通常用于GET请求

  • @RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的);GET方式无请求体,所以使用@RequestBody接收数据时,前端不能使用GET方式提交数据,而是用POST方式进行提交。在后端的同一个接收方法里,@RequestBody与@RequestBody可以同时使用,@RequestBody最多只能有一个,而@RequestParam()可以有多个。

  • 注解@RequestParam接收的参数是来自requestHeader中,即请求头通常用于GET请求,比如常见的url:http://localhost:8081/spring-boot-study/novel/findByAuthorAndType?author=唐家三少&type=已完结

  • @RequestParam有三个配置参数:

    • required 表示是否必须,默认为 true,必须。
    • defaultValue 可设置请求参数的默认值。
    • value 为接收url的参数名(相当于key值)。
  • 如果参数前写了@RequestParam(xxx),那么前端必须有对应的xxx名字才行(不管其是否有值,当然可以通
    过设置该注解的required属性来调节是否必须传),如果没有xxx名的话,那么请求会出错,报400。

  • 如果参数前不写@RequestParam(xxx)的话,那么就前端可以有可以没有对应的xxx名字才行,如果有xxx名
    的话,那么就会自动匹配;没有的话,请求也能正确发送。

  • @JsonAlias注解,实现:json转模型时,使json中的特定key能转化为特定的模型属性;但是模型转json时,
    对应的转换后的key仍然与属性名一致.

    @JsonAlias("Name","name123")
    private String name;
    
                  此时,json字符串转换为模型时,json中key为Name或为name123或为name的都能识别。
    

    结论②:@JsonProperty注解,实现:json转模型时,使json中的特定key能转化为指定的模型属性;同样的,模
    型转json时,对应的转换后的key为指定的key,见:示例中的motto字段的请求与响应。
    以下图进一步说明:

    @JsonProperty("name123")
    private String name;
    
               此时,json字符串转换为模型时,key为name123的能识别,但key为name的不能识别。
    

    结论③:@JsonAlias注解需要依赖于setter、getter,而@JsonProperty注解不需要。

    结论④:在不考虑上述两个注解的一般情况下,key与属性匹配时,默认大小写敏感。

    结论⑤:有多个相同的key的json字符串中,转换为模型时,会以相同的几个key中,排在最后的那个key的值给模
    型属性复制,因为setter会覆盖原来的值。见示例中的gender属性。

    结论⑥:后端@RequestBody注解对应的类在将HTTP的输入流(含请求体)装配到目标类(即:@RequestBody后面
    的类)时,会根据json字符串中的key来匹配对应实体类的属性,如果匹配一致且json中的该key对应的值
    符合(或可转换为)实体类的对应属性的类型要求时,会调用实体类的setter方法将值赋给该属性。

4.后端返回的msg如何显示到前端?

后端:

package com.wz.vhrdb.vo;

/**
 * @author: 王泽
 */

public class Result {
    private Boolean status =true;
    private String msg;

    public Boolean getStatus() {
        return status;
    }

    public void setStatus(Boolean status) {
        this.status = status;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}
@PostMapping("/adduser")
    public Result addUser(@RequestBody User user){
        Result result =new Result();
        try{
            userService.insertUser(user);
            result.setMsg("hr增加成功");
        }catch (Exception e){
            result.setStatus(false);
            result.setMsg("新增hr失败"+e.getMessage());
        }
        return result;

    }

前端:

 submitForm(){
      //提交表单到后端
      this.$http.post("http://localhost:8989/user/adduser",this.pForm).then(res=>{
        console.log(res.data);
        if(res.data.status){
          this.$message({
            message:'恭喜你'+res.data.msg,
            type:'success'
          })
        //  成功后的处理:清空表单信息,刷新所有
          this.pForm={};
          this.$emit('findAll');
        }else {
          this.$message.error(this.date.msg);
        }
      })
    },

5.后端传来的数据中嵌套“…”:[{“…”:“…”}]该怎么交互??

在这里插入图片描述

6.url中字组件使用父组件的表格行id?

问题等价于父组件向子组件传值!

  • 在父组件的 子组件标签 写要传的值

  • <span><put-user :id="uid"></put-user></span>
    
  • 在子组件中使用

  • props:['id'],
    
      methods: {
        submitForm() {
          console.log(this.id);
          this.$http.put("http://localhost:8989/users/"+this.id).then(res=>{
            console.log(res.data);
              this.$emit('findAll');
          })
        },
      }
    

7.登录的值是json,但是后台用的key values??怎么转换

导入 qs;
import qs from 'qs';

然后data 使用data:qs.stringify(param)
let params = {
  username: this.loginForm.username,
  password: this.loginForm.password
};
console.log(params);
let kv =qs.stringify(params);
console.log(kv);
this.$http.post('http://localhost:8989/login',kv)

8.关于项目的是否有状态登录

这前后端分离开发后,认证这一块到底是使用传统的 session 还是使用像 JWT 这样的 token 来解决呢?

这确实代表了两种不同的方向。

传统的通过 session 来记录用户认证信息的方式我们可以理解为这是一种有状态登录,而 JWT 则代表了一种无状态登录。可能有小伙伴对这个概念还不太熟悉,我这里就先来科普一下有状态登录和无状态登录。

8.1 什么是有状态

有状态服务,即服务端需要记录每次会话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如 Tomcat 中的 Session。例如登录:用户登录后,我们把用户的信息保存在服务端 session 中,并且给用户一个 cookie 值,记录对应的 session,然后下次请求,用户携带 cookie 值来(这一步有浏览器自动完成),我们就能识别到对应 session,从而找到用户的信息。这种方式目前来看最方便,但是也有一些缺陷,如下:

  • 服务端保存大量数据,增加服务端压力
  • 服务端保存用户状态,不支持集群化部署

8.2 什么是无状态

微服务集群中的每个服务,对外提供的都使用 RESTful 风格的接口。而 RESTful 风格的一个最重要的规范就是:服务的无状态性,即:

  • 服务端不保存任何客户端请求者信息
  • 客户端的每次请求必须具备自描述信息,通过这些信息识别客户端身份

那么这种无状态性有哪些好处呢?

  • 客户端请求不依赖服务端的信息,多次请求不需要必须访问到同一台服务器
  • 服务端的集群和状态对客户端透明
  • 服务端可以任意的迁移和伸缩(可以方便的进行集群化部署)
  • 减小服务端存储压力

8.3 如何实现无状态

无状态登录的流程:

  • 首先客户端发送账户名/密码到服务端进行认证
  • 认证通过后,服务端将用户信息加密并且编码成一个 token,返回给客户端
  • 以后客户端每次发送请求,都需要携带认证的 token
  • 服务端对客户端发送来的 token 进行解密,判断是否有效,并且获取用户登录信息

8.4 各自优缺点

使用 session 最大的优点在于方便。你不用做过多的处理,一切都是默认的即可。松哥本系列前面几篇文章我们也都是基于 session 来讲的。

但是使用 session 有另外一个致命的问题就是如果你的前端是 Android、iOS、小程序等,这些 App 天然的就没有 cookie,如果非要用 session,就需要这些工程师在各自的设备上做适配,一般是模拟 cookie,从这个角度来说,在移动 App 遍地开花的今天,我们单纯的依赖 session 来做安全管理,似乎也不是特别理想。

这个时候 JWT 这样的无状态登录就展示出自己的优势了,这些登录方式所依赖的 token 你可以通过普通参数传递,也可以通过请求头传递,怎么样都行,具有很强的灵活性。

第六章、总结

此项目是我写第一个前后端分离项目,项目功能很简单,但是遇到的问题很多,解决问题的过程让我对前后端分离的开发有了更深刻的理解,也让我对springsecurity的安全更加敬佩。对于前端vue中router以及axios的使用更加熟练,学会了ui组件库的使用方法。对于后端,对springboot以及springdatajpa和springsecurity都有新的收获!springboot+vue的开发方式更明确的定位了程序员的分工。但是一个出色的程序员我认为应该是对技术栈都有所掌握的。当然一种技术有更深的造诣是我们所追求的,但是知识面千万不能窄。这就是我本次项目开发的总结。

第七章、致谢

本次项目开发,感谢孙老师的课程,他的课程垫定了我的spring基础。还有百知教育的陈老师,讲的vue与elementui课程很好。感谢 王松(江南一点雨)的springboot与springsecurity以及springdatajpa,mybatis…的教学。让我真正的学会了开发知识!

第八章、参考文献

springboot官方文档

vue官方文档

axios官方文档

element ui 官方文档

vite官方文档

深入浅出springsecurity ——王松(江南一点雨)

MySQL官方文档

  • 35
    点赞
  • 146
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

结构化思维wz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值