数据库准备
CREATE DATABASE /*!32312 IF NOT EXISTS*/`mybatis` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */; USE `mybatis`; /*Table structure for table `department` */ CREATE TABLE `department` ( `did` int NOT NULL AUTO_INCREMENT, `departmentName` varchar(20) NOT NULL, PRIMARY KEY (`did`) ) ENGINE=InnoDB AUTO_INCREMENT=106 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; /*Data for the table `department` */ insert into `department`(`did`,`departmentName`) values (101,'教学部'),(102,'市场部'),(103,'教研部'),(104,'运营部'),(105,'后勤部'); /*Table structure for table `employee` */ CREATE TABLE `employee` ( `id` int NOT NULL AUTO_INCREMENT, `lastName` varchar(20) NOT NULL, `email` varchar(20) NOT NULL, `gender` int NOT NULL, `birth` date DEFAULT NULL, `department` int NOT NULL, PRIMARY KEY (`id`), KEY `did` (`department`), CONSTRAINT `did` FOREIGN KEY (`department`) REFERENCES `department` (`did`) ) ENGINE=InnoDB AUTO_INCREMENT=1015 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; /*Data for the table `employee` */ insert into `employee`(`id`,`lastName`,`email`,`gender`,`birth`,`department`) values (1001,'AA','121456qq.com',1,'2021-03-09',101),(1003,'BB','124561@qq.com',0,'2021-05-03',105),(1005,'zhangsan','1210418430@qq.com',1,'2021-07-04',102),(1006,'zhangsan','1210418430@qq.com',1,'2021-07-04',102),(1009,'zhangsans','a123456yy@qq.com',1,'2000-02-02',102),(1010,'20210704','14565430@qq.com',0,'2000-02-02',105),(1011,'djfihasf\'','1214546@qq.com',0,'2000-02-02',103),(1014,'sadasdsad','456430@qq.com',0,'2000-02-02',103); /*Table structure for table `user` */ CREATE TABLE `user` ( `id` int NOT NULL, `name` varchar(20) DEFAULT NULL, `pwd` varchar(30) DEFAULT NULL, `perms` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*Data for the table `user` */ insert into `user`(`id`,`name`,`pwd`,`perms`) values (1,'java','java','user:add'),(2,'1210','1210','user:add'),(3,'wsad','1210','user:add'),(5,'c++','1210','user:add'),(6,'张三','1210','user:update'),(7,'张思','1210','user:update'),(8,'李四','1210','user:update'),(9,'root','123','user:update');
基本环境搭建
新建一个Springboot项目,导入pom依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.myproject</groupId> <artifactId>springbootproject01</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springbootproject01</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> </dependency> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-java8time</artifactId> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.7.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.12</version> </dependency> <!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro --> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.3</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
建立基本结构和配置框架
application.properties
spring.thymeleaf.cache=false spring.messages.basename=i18n.login spring.mvc.format.date=yyyy-MM-dd spring.datasource.username=root spring.datasource.password=1234 spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.type=com.alibaba.druid.pool.DruidDataSource mybatis.type-aliases-package=com.myproject.pojo mybatis.mapper-locations=classpath:mybatis/*.xml
package
com.myproject.config
SecurityConfigTest
com.myproject.controller
EmployeeController
com.myproject.mapper
DepartmentMapper
DepartmentMapperImpl
EmployeeMapper
EmployeeMapperImpl
UserMapper
UserMapperImpl
com.myproject.pojo
Department
Employee
EmployeeWeb
Users
com.myproject.service
DepartmentService
DepartmentServiceImpl
EmployeeService
EmployeeServiceImpl
MyUserDetailsService
UserService
UserServiceImpl
resources
mybatis
DepartmentMapper.xml
EmployeeMapper.xml
UserMapper.xml
static(前端资源)
templates(前端资源)
安全管理框架(SpringSecurity)
MyUserDetailsService.java主要用接收传递用户名密码。
package com.myproject.service; import com.myproject.mapper.UserMapper; import com.myproject.pojo.Users; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; import java.util.List; @Component @Service("userDetailsService") public class MyUserDetailsService implements UserDetailsService { /** * 实现UserDetailsService中的loadUserByUsername方法 * loadUserByUsername会从表单中获取name为"username"和"password"的字段, * 实现此方法会传入username来让我们验证,即我们从数据库中查询这个用户是否存在, * 然后将查询到的密码传给BCryptPasswordEncoder对象,这个对象会自动帮我们去验证密码正确 * BCryptPasswordEncoder采用SHA-256 +随机盐+密钥对密码进行加密,这个方法不能传入明文密码 */ @Autowired UserMapper userMapper; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { Users user = userMapper.queryUserByName(username);//从数据库获取User对象 if (user==null){//如果用户并不存在则会抛出异常 throw new UsernameNotFoundException("用户名不存在"); } List<GrantedAuthority> role = AuthorityUtils.commaSeparatedStringToAuthorityList("role"); //设置用户角色 // 我们可以从User的构造器中看到,User中需要传入三个值,即用户名,加密的密码和用户角色 // public User(String username, String password, Collection<? extends GrantedAuthority> authorities) return new User(user.getName() ,new BCryptPasswordEncoder().encode(user.getPwd()),role); //这个User是package org.springframework.security.core.userdetails下的User,并不是我们的实体类对象。 } }
SecurityConfigTest.java主要设置用户权限,即授权。
package com.myproject.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; 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.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import sun.security.util.Password; @Configuration public class SecurityConfigTest extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(password()); //在 Spring Security 中,用来处理身份认证的类是 AuthenticationManager,我们也称之为认证管理器。 //而AuthenticationManagerBuilder用来生成一个AuthenticationManager, //passwordEncoder需要一个BCryptPasswordEncoder,即下面这个方法生成一个BCryptPasswordEncoder返回给AuthenticationManagerBuilder } @Bean PasswordEncoder password(){ return new BCryptPasswordEncoder();//生成一个BCryptPasswordEncoder创个上面的passwordEncoder } @Override protected void configure(HttpSecurity http) throws Exception { http.formLogin() .loginPage("/")//设置登录页面 .loginProcessingUrl("/user/login")//表单传递的地址,即action .defaultSuccessUrl("/emps").permitAll()//登录成功跳转地址 .and().authorizeRequests().antMatchers("/").permitAll()//放行的页面,一般登录页面需要放行 .anyRequest().authenticated()//任何请求都必须经过身份验证,否则返回401响应 .and().csrf().disable()//Cross-site request forgery跨站请求伪造,也被称为“One Click Attack”或者Session Riding, // 通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。为了防止跨站提交攻击,通常会配置csrf。但也会影响性能,可自由选择开启或关闭 ; http.logout().logoutUrl("/user/logout")//注销 .logoutSuccessUrl("/").permitAll(); } }
EmployeeController.java
package com.myproject.controller; import com.myproject.mapper.DepartmentMapperImpl; import com.myproject.pojo.Department; import com.myproject.pojo.Employee; import com.myproject.pojo.EmployeeWeb; import com.myproject.service.DepartmentServiceImpl; import com.myproject.service.EmployeeServiceImpl; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpSession; import java.util.*; @Controller public class EmployeeController { @Autowired private EmployeeServiceImpl employeeService; @Autowired private DepartmentServiceImpl departmentService; /** * * @param model * @return * 这个方法就是登录后要跳转的Servlet,这个方法会拿到全部的员工信息,然后返回给list.html页面。 */ @RequestMapping("/emps") public String list(Model model){ List<Employee> employees = employeeService.queryAll(); List<EmployeeWeb> list=new ArrayList(); for (int i=0;i<employees.size();i++){ EmployeeWeb employeeWeb=new EmployeeWeb(); List<Department> depAll = departmentService.getDepAll(); int department = employees.get(i).getDepartment(); int id = employees.get(i).getId(); String lastName = employees.get(i).getLastName(); int gender = employees.get(i).getGender(); String email = employees.get(i).getEmail(); Date birth = employees.get(i).getBirth(); for (int j=0;j<depAll.size();j++){ int did = depAll.get(j).getDid(); if (did==department){ employeeWeb.setDepartment(depAll.get(j)); } } employeeWeb.setId(id); employeeWeb.setLastName(lastName); employeeWeb.setBirth(birth); employeeWeb.setEmail(email); employeeWeb.setGender(gender); list.add(employeeWeb); } model.addAttribute("emps",list); for (EmployeeWeb employeeWeb : list) { System.out.println(employees); } return "emp/list"; } /** * * @param model * @return * 这个方法会获取获得所有的部门对象,然后传给add页面,因为部门选项需要所有的部门信息。 */ @GetMapping("/emp") public String toAllpage(Model model){ List<Department> depList = departmentService.getDepAll(); model.addAttribute("departments",depList); return "emp/add"; } /** * * @param employee * @return * 这个方法会从add页面获取需要添加的员工信息,然后将数据传回数据库,完成后悔重定向到所有成员的列表页面。 */ @PostMapping("/emp") public String addEmp(Employee employee){ System.out.println(employee); Boolean aBoolean = employeeService.saveEmp(employee); System.out.println(aBoolean); return "redirect:/emps"; } /** * * @param id * @param model * @return * 这个方法是更新员工信息跳转的Servlet,这个方法需要先得到需要更新的员工id, * 然后传回到更新页面,同时这个方法也需要查询出全部的部门信息,传回给跟新页面, * 同样是部门选项需要全部的部门信息,然后跳转到更新页面。 */ @GetMapping("/toUpdate/{id}") public String toUpdate(@PathVariable("id")Integer id, Model model){ System.out.println("toUpdate"); Employee employee = employeeService.queryById(id); model.addAttribute("employee",employee); //查询所有部门信息 List<Department> depAll = departmentService.getDepAll(); model.addAttribute("departments",depAll); return "emp/update"; } /** * * @param employee * @return * 这个方法会从前端会的一个员工新的信息,完成员工的信息更新,然后重定向到所有成员的列表页面。 */ @PostMapping("/updateUser") public String Update(Employee employee){ employeeService.saveEmp(employee); return "redirect:/emps"; } /** * * @param id * @return * 这个方法会从前端得到一个员工id,执行删除,然后重定向到所有成员的列表页面。 */ @GetMapping("/delemp/{id}") public String delemp(@PathVariable("id")Integer id){ employeeService.deleteEmp(id); return "redirect:/emps"; } //如果登录的用户没有权限执行某个操作,则会跳转到此方法,该方法未完成。 @RequestMapping("/noauth") @ResponseBody public String unauthorized(){ return "未授权无法访问此页面"; } //已被SpringSecurity解决 // @RequestMapping("/user/logout") // public String logOut(HttpSession session){ // session.invalidate(); // return "redirect:/index.html"; // } }
页面
总结
这个项目是采用SpringBoot开发的,SpringBoot相较于Spring省略了很多配置,取而代之的是通过注解来完成IOC,极大地简化了开发过程,除了SpringBoot,这个项目还采用了SpringSecurity来完成用户登录的授权和认证。
项目链接:https://gitee.com/coopermini/employee-management-system