SpringBoot和Vue.js实现的前后端分离的用户权限管理系统

2023-05-14 08:05:18 后端 管理系统 用户权限

后端实现

1. 数据库设计

我们需要设计两个表:用户表和角色表。

用户表

字段 类型 描述
id bigint(20) 用户 ID
username varchar(50) 用户名
passWord varchar(255) 密码
email varchar(50) 邮箱
phone varchar(20) 手机号码
create_by varchar(50) 创建人
create_time datetime 创建时间
update_by varchar(50) 最后修改人
update_time datetime 最后修改时间
status tinyint(1) 用户状态

角色表

字段 类型 描述
id bigint(20) 角色 ID
role_name varchar(50) 角色名称
create_by varchar(50) 创建人
create_time datetime 创建时间
update_by varchar(50) 最后修改人
update_time datetime 最后修改时间
status tinyint(1) 角色状态

用户角色表

字段 类型 描述
id bigint(20) ID
user_id bigint(20) 用户 ID
role_id bigint(20) 角色 ID
create_by varchar(50) 创建人
create_time datetime 创建时间
update_by varchar(50) 最后修改人
update_time datetime 最后修改时间
status tinyint(1) 状态

2. 创建 Maven 项目

使用 Maven 创建 Spring Boot 项目,引入 spring Boot WEB、Spring Boot JPA、Mysql 和 Lombok 等依赖。可以使用以下命令创建 Maven 项目:

mvn archetype:generate -DgroupId=com.example -dartifactId=user-management -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

3. 配置数据库

在 application.properties 文件中添加以下配置:

spring.datasource.url=jdbc:mysql://localhost:3306/user_management?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.fORMat_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true

4. 创建实体类

创建 User、Role 和 UserRole 实体类,使用 Lombok 注解简化代码。具体代码如下:

User.java

@Data
@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id
User.java(续)
java
Copy code
    private String username;
    private String password;
    private String email;
    private String phone;
    private String createBy;
    private Date createTime;
    private String updateBy;
    private Date updateTime;
    private Boolean status;
}

Role.java

@Data
@Entity
@Table(name = "role")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String roleName;
    private String createBy;
    private Date createTime;
    private String updateBy;
    private Date updateTime;
    private Boolean status;
}

UserRole.java

@Data
@Entity
@Table(name = "user_role")
public class UserRole {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private Long userId;
    private Long roleId;
    private String createBy;
    private Date createTime;
    private String updateBy;
    private Date updateTime;
    private Boolean status;
}

5. 创建 Repository

创建 UserRepository、RoleRepository 和 UserRoleRepository,用于操作数据库。

UserRepository.java

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    User findByUsername(String username);
}

RoleRepository.java

@Repository
public interface RoleRepository extends JpaRepository<Role, Long> {
}

UserRoleRepository.java

@Repository
public interface UserRoleRepository extends JpaRepository<UserRole, Long> {
    List<UserRole> findByUserId(Long userId);
}

6. 创建 Service

创建 UserService、RoleService 和 UserRoleService,用于处理业务逻辑。

UserService.java

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private UserRoleRepository userRoleRepository;

    public User findByUsername(String username) {
        return userRepository.findByUsername(username);
    }

    public List<UserRole> findUserRolesByUserId(Long userId) {
        return userRoleRepository.findByUserId(userId);
    }
}

RoleService.java

@Service
public class RoleService {
    @Autowired
    private RoleRepository roleRepository;
}

UserRoleService.java

@Service
public class UserRoleService {
    @Autowired
    private UserRoleRepository userRoleRepository;
}

7. 创建 Controller

创建 UserController、RoleController 和 UserRoleController,用于处理请求。

UserController.java

@RestController
@RequestMapping("/api/user")
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping("/{username}")
    public User findByUsername(@PathVariable String username) {
        return userService.findByUsername(username);
    }

    @GetMapping("/{userId}/roles")
    public List<UserRole> findUserRolesByUserId(@PathVariable Long userId) {
        return userService.findUserRolesByUserId(userId);
    }
}

RoleController.java

@RestController
@RequestMapping("/api/role")
public class RoleController {
    @Autowired
    private RoleService roleService;

    @GetMapping("")
    public List<Role> findAll() {
        return roleService.findAll();
    }
}

UserRoleController.java

@RestController
@RequestMapping("/api/userRole")
public class UserRoleController {
    @Autowired
    private UserRoleService userRoleService;
}

8. 启动应用

使用 Maven 命令 mvn spring-boot:run 启动应用,或者直接运行 UserManagementApplication 类。

9. 完整的SecurityConfig.java:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    private JwtAuthenticationEntryPoint unauthorizedHandler;

    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() {
        return new JwtAuthenticationFilter();
    }

    @Override
    public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder
                .userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder());
    }

    @Bean(BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(httpsecurity Http) throws Exception {
        http
                .cors()
                    .and()
                .csrf()
                    .disable()
                .exceptionHandling()
                    .authenticationEntryPoint(unauthorizedHandler)
                    .and()
                .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                .authorizeRequests()
                    .antMatchers("/api/auth/**")
                        .permitAll()
                    .anyRequest()
                        .authenticated();

        http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}

其中,JwtAuthenticationFilter是前面提到的JWT认证过滤器,UserDetailsServiceImpl是实现了Spring Security的UserDetailsService接口的用户服务类,JwtAuthenticationEntryPoint是未经授权时抛出的异常处理程序。配置类中的configure方法配置了授权规则,访问“/api/auth/**”路径下的接口不需要认证即可访问,其他所有请求都需要认证后才能访问。同时,我们也添加了JWT认证过滤器。

前端实现

1. 创建 Vue.js 项目

使用 Vue CLI 创建项目:

vue create user-management

2. 添加依赖

在 package.JSON 中添加以下依赖:

{
  "dependencies": {
    "axios": "^0.21.1",
    "element-plus": "^1.0.2-beta.55",
    "vue": "^2.6.12",
    "vue-router": "^3.5.1"
  }
}

然后执行 npm install 安装依赖。

3. 配置 Axios

在 src/main.js 中添加以下代码:

import axios from 'axios'
axios.defaults.baseURL = 'http://localhost:8080/api'
Vue.prototype.$http = axios

4. 创建路由

在 src/router/index.js 中添加以下代码:

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Login from '../views/Login.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/login',
    name: 'Login',
    component: Login
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

5. 创建页面

在 src/views 目录下创建以下页面:

Home.vue

<template>
  <div>
    <h1>用户管理</h1>
    <table>
      <thead>
        <tr>
          <th>用户名</th>
          <th>邮箱</th>
          <th>手机号码</th>
          <th>操作</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="user in users" :key="user.id">
          <td>{{ user.username }}</td>
          <td>{{ user.email }}</td>
          <td>{{ user.phone }}</td>
          <td>
            <button @click="editUser(user)">编辑</button>
            <button @click="deleteUser(user)">删除</button>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
export default {
  name: 'Home',
  data() {
    return {
      users: []
    }
  },
  mounted() {
    this.loadUsers()
  },
  methods: {
    loadUsers() {
      this.$http.get('/user')
        .then(response => {
          this.users = response.data
        })
        .catch(error => {
          console.log(error)
        })
    },
    editUser(user) {
      console.log(user)
    },
    deleteUser(user) {
      console.log(user)
    }
  }
}
</script>

Login.vue

<template>
  <div>
    <h1>登录</h1>
    <form @submit.prevent="login">
      <div>
        <label>用户名:</label>
        <input type="text" v-model="username" />
      </div>
      <div>
        <label>密码:</label>
        <input type="password" v-model="password" />
      </div>
      <button type="submit">登录</button>
    </form>
  </div>
</template>

<script>
export default {
  name: 'Login',
  data() {
    return {
      username: '',
      password: ''
    }
  },
  methods: {
    login() {
      const params = {
        username: this.username,
        password: this.password
  }

  this.$http.post('/login', params)
    .then(response => {
      console.log(response)
    })
    .catch(error => {
      console.log(error)
    })
}
}
}
</script>

6. 添加 Element UI 组件

在 src/main.js 中添加以下代码:

import ElementPlus from 'element-plus'
import 'element-plus/dist/index.CSS'

Vue.use(ElementPlus)

7. 运行项目

在项目根目录下执行以下命令运行项目:

npm run serve

然后在浏览器中访问 http://localhost:8080 查看效果。

以上代码只是简单地实现了用户列表的显示以及登录功能的实现,还需要进一步完善和优化。同时,还需要在后端实现相应的接口。

8. 添加路由

在 src/router/index.js 中添加以下代码:

import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../views/Login.vue'
import UserList from '../views/UserList.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    redirect: '/login'
  },
  {
    path: '/login',
    name: 'Login',
    component: Login
  },
  {
    path: '/userlist',
    name: 'UserList',
    component: UserList,
    meta: {
      requireAuth: true
    }
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

其中,meta 属性中的 requireAuth 表示该路由需要登录才能访问。

9. 添加登录拦截

在 src/main.js 中添加以下代码:

router.beforeEach((to, from, next) => {
  if (to.meta.requireAuth && !localStorage.getItem('token')) {
    next({
      path: '/login',
      query: { redirect: to.fullPath }
    })
  } else {
    next()
  }
})

这段代码的作用是,在用户访问需要登录才能访问的路由时,检查用户是否已登录。如果用户未登录,则跳转到登录页面,并将目标路由的路径保存到查询参数中,以便用户登录成功后自动跳转到目标路由。

10. 添加用户服务

在 src/services 目录下创建 UserService.js 文件,并添加以下代码:

import axios from 'axios'

const API_URL = '/api/users/'

class UserService {
  getUsers () {
    return axios.get(API_URL)
  }

  getUser (id) {
    return axios.get(API_URL + id)
  }

  createUser (data) {
    return axios.post(API_URL, data)
  }

  updateUser (id, data) {
    return axios.put(API_URL + id, data)
  }

  deleteUser (id) {
    return axios.delete(API_URL + id)
  }
}
export default new UserService()

该文件定义了一个 UserService 类,用于向后端发送请求,获取用户数据。其中,API_URL 表示后端 API 的根路径,getUsers、getUser、createUser、updateUser 和 deleteUser 方法分别对应获取用户列表、获取单个用户、创建用户、更新用户和删除用户。

11. 添加用户列表页面

在 src/views 目录下创建 UserList.vue 文件,并添加以下代码:

<template>
  <div>
    <h1>用户列表</h1>
    <div class="mb-3">
      <router-link to="/user/add" class="btn btn-primary">添加用户</router-link>
    </div>
    <table class="table table-striped">
      <thead>
        <tr>
          <th>用户名</th>
          <th>邮箱</th>
          <th>角色</th>
          <th>操作</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="user in userList" :key="user.id">
          <td>{{ user.username }}</td>
          <td>{{ user.email }}</td>
          <td>{{ user.roles.map(role => role.name).join(', ') }}</td>
          <td>
            <router-link :to="'/user/edit/' + user.id" class="btn btn-primary">编辑</router-link>
            <button class="btn btn-danger" @click="deleteUser(user.id)">删除</button>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
import axios from "axios";

export default {
  data() {
    return {
      userList: [],
    };
  },
  methods: {
    fetchUserList() {
      axios.get("/api/private/users").then((response) => {
        this.userList = response.data;
      });
    },
    deleteUser(id) {
      if (confirm("确定删除该用户吗?")) {
        axios.delete(`/api/private/users/${id}`).then(() => {
          this.fetchUserList();
        });
      }
    },
  },
  mounted() {
    this.fetchUserList();
  },
};
</script>

这个文件中,我们使用了vue-router和axios库。在fetchUserList方法中,我们使用axios库发起了一个GET请求来获取用户列表。在deleteUser方法中,我们使用axios库发起了一个DELETE请求来删除用户。

接下来我们将完成系统的权限控制部分。在Spring Security中,权限控制通常通过定义安全配置类来实现。

首先,我们需要创建一个实现了WebSecurityConfigurer接口的安全配置类SecurityConfig。在这个类中,我们可以配置用户认证和授权规则。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 
    @Autowired
    private UserDetailsService userDetailsService;
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/api/public/**").permitAll()
                .antMatchers("/api/private/**").authenticated()
                .and().formLogin()
                .loginPage("/login")
                .permitAll()
                .defaultSuccessUrl("/")
                .and().loGout().logoutUrl("/logout").logoutSuccessUrl("/login").invalidateHttpSession(true);
    }
 
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
 
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

在上述代码中,我们配置了以下规则:

  • 所有以/api/public开头的请求都不需要认证即可访问。
  • 所有以/api/private开头的请求都需要认证才能访问。
  • 登录页面为/login,登录成功后默认跳转到根路径/。
  • 注销路径为/logout,注销成功后跳转到登录页面。
  • 在AuthenticationManagerBuilder中指定了用户认证服务和密码加密方式。

为了实现用户认证,我们需要创建一个实现了UserDetailsService接口的用户认证服务。UserDetailsService是一个Spring Security的核心接口,用于查询用户信息。我们可以通过重写该接口的loadUserByUsername方法来实现自定义的用户认证逻辑。

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
 
    @Autowired
    private UserRepository userRepository;
 
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("用户不存在");
        }
        List<SimpleGrantedAuthority> authorities = user.getRoles().stream()
                .map(role -> new SimpleGrantedAuthority(role.getName()))
                .collect(Collectors.toList());
        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(),
                authorities);
    }
}

在上述代码中,我们通过注入UserRepository来查询用户信息。然后,我们将查询到的用户角色信息转换为Spring Security的授权信息,并返回一个UserDetails对象。

至此,我们已经完成了用户认证和权限控制的实现。通过这个系统,我们可以实现用户登录、注销和权限控制等基础功能。

12. 完整的AddUser.vue组件代码:

<template>
  <div>
    <h2>Add User</h2>
    <form>
      <div class="form-group">
        <label for="username">Username</label>
        <input type="text" class="form-control" id="username" v-model="user.username" required>
      </div>
      <div class="form-group">
        <label for="email">Email</label>
        <input type="email" class="form-control" id="email" v-model="user.email" required>
      </div>
      <div class="form-group">
        <label for="password">Password</label>
        <input type="password" class="form-control" id="password" v-model="user.password" required>
      </div>
      <div class="form-group">
        <label for="roles">Roles</label>
        <select multiple class="form-control" id="roles" v-model="user.roles">
          <option v-for="role in roles" :key="role.id" :value="role">{{ role.name }}</option>
        </select>
      </div>
      <button type="button" class="btn btn-primary" @click="addUser">Add User</button>
    </form>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  name: 'AddUser',
  data() {
    return {
      user: {
        username: '',
        email: '',
        password: '',
        roles: []
      },
      roles: []
    }
  },
  created() {
    axios.get('/api/roles')
      .then(response => {
        this.roles = response.data;
      })
      .catch(error => {
        console.log(error);
      });
  },
  methods: {
    addUser() {
      axios.post('/api/users', this.user)
        .then(response => {
          this.$router.push({ name: 'UserList' });
        })
        .catch(error => {
          console.log(error);
        });
    }
  }
}
</script>

<style scoped>
.form-group {
  margin-bottom: 1rem;
}
</style>

在这个组件中,我们使用了Bootstrap的样式来美化表单。我们从后端获取了所有的角色列表,将其渲染到了下拉列表中。同时,我们绑定了一个addUser方法,在点击“Add User”按钮时将用户信息提交到后端创建一个新用户。13. 完整的UserForm.vue组件代码:

<template>
  <div>
    <form>
      <div class="form-group">
        <label for="username">Username</label>
        <input type="text" class="form-control" id="username" v-model="user.username" required>
      </div>
      <div class="form-group">
        <label for="email">Email</label>
        <input type="email" class="form-control" id="email" v-model="user.email" required>
      </div>
      <div class="form-group">
        <label for="password">Password</label>
        <input type="password" class="form-control" id="password" v-model="user.password" required>
      </div>
      <div class="form-group">
        <label for="roles">Roles</label>
        <select multiple class="form-control" id="roles" v-model="user.roles">
          <option v-for="role in roles" :key="role.id" :value="role">{{ role.name }}</option>
        </select>
      </div>
      <button type="button" class="btn btn-primary" @click="submitForm">{{ submitButtonLabel }}</button>
    </form>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  name: 'UserForm',
  props: {
    user: {
      type: Object,
      default: () => {
        return {
          username: '',
          email: '',
          password: '',
          roles: []
        };
      }
    },
    roles: {
      type: Array,
      default: () => {
        return [];
      }
    },
    submitButtonLabel: {
      type: String,
      default: 'Submit'
    },
    onSubmit: {
      type: Function,
      default: () => {
        console.log('Submit function not provided');
      }
    }
  },
  methods: {
    submitForm() {
      this.onSubmit(this.user);
    }
  }
}
</script>

<style scoped>
.form-group {
  margin-bottom: 1rem;
}
</style>

在这个组件中,我们使用了Bootstrap的样式来美化表单。我们从父组件中传递了一个user对象、roles数组和submitButtonLabel属性,分别用于渲染表单元素和提交按钮。同时,我们使用了props属性来声明这些属性。最后,我们绑定了一个submitForm方法,在点击提交按钮时将用户信息传递给父组件的onSubmit方法处理。

到此这篇关于SpringBoot和Vue.js实现的前后端分离的用户权限管理系统的文章就介绍到这了,更多相关SpringBoot Vue.js用户权限管理系统内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

相关文章