人事管理系统是每个公司必备的管理系统,可以更方便的管理员工。
hr实现对员工的增删改查,管理员实现对hr的更改。对员工进行搜索等功能。
本项目是前后端分离项目,在服务器中运行。前端页面友好。
该系统主要是前端设计页面接受后端的JSON数据,前后端所有数据除了登录页面使用key values 形式,其余全部使用JSON格式。后端使用springdatajpa自动生成的restful风格接口。前端通过axios发起请求获取接口数据。但是在开发中,我们需要克服跨域所带来的的问题。部署阶段就不存在跨域,因为我们会把前端打包到后端,一起部署。这样就不涉及到跨域问题了。当然也可以使用nigx进行代理。
员工表:
hr表:
角色表:
添加springdata rest
依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-restartifactId>
dependency>
目录结构
创建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!
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
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')
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>
在前后端分离这样的开发架构下,前后端的交互都是通过 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();
})
只有开发时存在这种跨域的情况。
解决思路:
https://my.oschina.net/u/1020373/blog/4899705
前端接收:发送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方法将值赋给该属性。
后端:
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);
}
})
},
问题等价于父组件向子组件传值!
在父组件的 子组件标签
写要传的值
<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');
})
},
}
导入 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)
这前后端分离开发后,认证这一块到底是使用传统的 session 还是使用像 JWT 这样的 token 来解决呢?
这确实代表了两种不同的方向。
传统的通过 session 来记录用户认证信息的方式我们可以理解为这是一种有状态登录,而 JWT 则代表了一种无状态登录。可能有小伙伴对这个概念还不太熟悉,我这里就先来科普一下有状态登录和无状态登录。
有状态服务,即服务端需要记录每次会话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如 Tomcat 中的 Session。例如登录:用户登录后,我们把用户的信息保存在服务端 session 中,并且给用户一个 cookie 值,记录对应的 session,然后下次请求,用户携带 cookie 值来(这一步有浏览器自动完成),我们就能识别到对应 session,从而找到用户的信息。这种方式目前来看最方便,但是也有一些缺陷,如下:
微服务集群中的每个服务,对外提供的都使用 RESTful 风格的接口。而 RESTful 风格的一个最重要的规范就是:服务的无状态性,即:
那么这种无状态性有哪些好处呢?
无状态登录的流程:
使用 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官方文档