办公自动化OA系统
1、办公自动化系统(Office Automation)是替代传统办公的解决方案
2、OA系统是利用软件技术构建的单位内部办公平台,用于辅助办公
3、利用OA系统可将办公数据数字化,可极大提高办公流程执行效率
第一步创建maven管理项目
第二部进行web工程设置
配置Tomcat
添加一个.html文件运行测试一下看看有没有问题
第三部 配置pop.xml 集成一系列组件
<?xml version="1.0" encoding="UTF-8"?><project xmlns="/POM/4.0.0"xmlns:xsi="/2001/XMLSchema-instance"xsi:schemaLocation="/POM/4.0.0 /xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.imooc</groupId><artifactId>oa</artifactId><version>1.0-SNAPSHOT</version><repositories><repository><id>aliyun</id><name>aliyun</name><url>/repository/public</url></repository></repositories><dependencies><!--Mybatis框架--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.2</version></dependency><!--MySql8JDBC驱动--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.17</version></dependency><!--Druid数据库连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.14</version></dependency><!--logback日志框架--><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version></dependency><!--Junit测试框架--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency></dependencies><!-- <build>--><!-- <plugins>--><!-- <plugin>--><!--<!–利用Maven编译插件将编译级别提高到1.8解决–>--><!--<groupId>org.apache.maven.plugins</groupId>--><!--<!–maven-compiler-plugin是Maven自带的编译插件–>--><!--<artifactId>maven-release-plugin</artifactId>--><!--<version>3.3</version>--><!--<configuration>--><!--<!–检查源码按照1.8规则,默认1.5–>--><!--<source>1.8</source>--><!--<!–按4.8规则生成字节码–>--><!--<target>1.8</target>--><!--</configuration>--><!-- </plugin>--><!-- </plugins>--><!-- </build>--><properties><piler.source>9</piler.source><piler.target>9</piler.target></properties></project>
创建并且编写mybatis-config.xml核心配置文件
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configurationPUBLIC "-//com.//DTD Config 3.0//EN""/dtd/mybatis-3-config.dtd"><configuration><settings><!--驼峰命名转换 form_id -> formId--><setting name="mapUnderscoreToCamelCase" value="true"/></settings><environments default="dev"><!--开发环境配置--><environment id="dev"><!--事务管理器采用JDBC方式--><transactionManager type="JDBC"/><!--利用MyBatis自带连接池进行管理--><dataSource type="POOLED"><!--JDBC连接属性--><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url"value="jdbc:mysql://localhost:3306/imooc-oa?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"/><property name="username" value="root"/><property name="password" value="youbenshashi"/></dataSource></environment></environments><mappers><mapper resource="mappers/test.xml"/></mappers></configuration>
创建MybatisUtils工具类
这里用到了Lambda表达式
lambda表达式
private static SqlSessionFactory sqlSessionFactory = null;static {Reader reader = null;try {// 加载配置文件。初始化sqlSessionFactoryreader = Resources.getResourceAsReader("mybatis-config.xml");sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);} catch (IOException e) {// 初始化错误throw new ExceptionInInitializerError(e);}}/*** 执行Select查询SQL* 这里使用了Function类,使用Lambda表达式更方便的实现SQL语句* @param func 要执行查询语句的代码块* @return 查询结果*/public static Object excuteQuery(Function<SqlSession, Object> func) {SqlSession sqlSession = sqlSessionFactory.openSession();try {return func.apply(sqlSession);} finally {sqlSession.close();}}/*** 执行Insert/update/delete写操作SQL* @param func 要执行写操作的代码块* @return 写操作后返回的结果*/public static Object excuteUpdate(Function<SqlSession, Object> func) {// 需要手动提交事务,将自动提交关闭SqlSession sqlSession = sqlSessionFactory.openSession(false);try {Object obj = func.apply(sqlSession);// 操作成功 提交事务mit();return obj;} catch (Exception e) {// 操作失败,回滚sqlSession.rollback();throw e;} finally {// 关闭sqlSessionsqlSession.close();}}
MyBatis整合Druid(alibaba)连接池
在java目录下创建一个用来初始化连接池的类
public class DruidDataSourceFactory extends UnpooledDataSourceFactory {public DruidDataSourceFactory() {// 将配置相关属性(xml)给dataSourcethis.dataSource = new DruidDataSource();}// 在这里初始化各种连接池(是否重写这个方法是需要根据使用的连接池属性来判断的)@Overridepublic DataSource getDataSource() {try {((DruidDataSource) this.dataSource).init();//初始化druid数据源} catch (SQLException e) {throw new RuntimeException(e);}return dataSource;}}
修改mybatis-config.xml配置文件
<!--MyBatis与Druid的整合--><!-- 上方自定义的类--><dataSource type="com.imooc.oa.datasource.DruidDataSourceFactory"><!--JDBC连接属性--><!-- driver修改为driverClassName 其他的不需要修改 --><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/><property name="url"value="jdbc:mysql://localhost:3306/imooc-oa?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"/><property name="username" value="root"/><property name="password" value="mima"/><!-- 初始连接数量 --><property name="initialSize" value="10"/><!-- 最大连接数量 --><property name="maxActive" value="20"/></dataSource>
运行查看是否配置正确
使用IDEA设置webapp目录的方法
整合Freemarker
在pop.xml增加Freemarker的依赖
<dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.29</version></dependency><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><!--依赖只参与编译测试,不进行发布--><scope>provided</scope></dependency>
在web.xml文件中对Freemarker的配置
<servlet><!--FreemarkerServlet用于读取解析ftl文件--><servlet-name>freemarker</servlet-name><servlet-class>freemarker.ext.servlet.FreemarkerServlet</servlet-class><init-param><!--定义模板的存储路径--><param-name>TemplatePath</param-name><param-value>/WEB-INF/ftl</param-value></init-param><init-param><!--default_encoding用于设置读取ftl文件采用的字符集,避免中文乱码--><param-name>default_encoding</param-name><param-value>UTF-8</param-value></init-param></servlet><!--增加映射地址--><servlet-mapping><servlet-name>freemarker</servlet-name><url-pattern>*.ftl</url-pattern></servlet-mapping>
在配置的目录下创建ftl文件夹,把.html文件放入到该文件夹中并且修改.html为.ftl
将资源发布到lib包下,每次修改依赖后都要进行修改
增加logblack配置文件
在resources下添加配置文件
<?xml version="1.0" encoding="UTF-8" ?><configuration><appender name="console" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender><!--日志输出级别(优先级高到低):error:错误–系统的故障日志warn:警告-存在风险或使用不当的日志info:一般性消息debug:程序内部用于调试信息trace:程序运行的跟踪信息--><root level="debug"><appender-ref ref="console"/></root></configuration>
开发基于RBAC的权限控制模块
RBAC(Role-Based Access Control)-基于角色的访问控制
1.基于角色权限控制(RBAC)是面向企业安全策略的访问控制方式
2.RBAC核心思想是将控制访问的资源与角色(Role)进行绑定
3.系统的用户(User)与角色(Role)再进行绑定,用户便拥有对应权限
所有功能、资源都与角色(核心)进行绑定,系统用户对应角色,从而系统角色对应不同的资源,一个公司员工至少有一个系统用户账号。角色表是最重要的。
创建相关数据库
1.创建角色表(sys_role)
主键类型设置为Bigint 防止特殊情况。
2.创建资源(功能)表 (sys_node)
3.创建角色与资源(功能)的对应表(sys_role_node)
4.部门表(adm_department)
5.员工表(adm_employee)
6.用户表(sys_user)
7.用户与角色对应表(sys_role_user)
实现用户登录(前端)
基于LayUI开发登录页,需要在LayUI官网下载,然后将需要的文件粘贴到自己的项目中。
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>办公OA系统</title><link rel="stylesheet" href="/resources/layui/css/layui.css"><style>body {background-color: #F2F2F2;}.oa-container {/*background-color: white;*/position: absolute;width: 400px;height: 350px;top: 50%;left: 50%;padding: 20px;margin-left: -200px;margin-top: -175px;}#username, #password {text-align: center;font-size: 24px;}</style></head><body><div class="oa-container"><h1 style="text-align: center;margin-bottom: 20px">办公OA系统</h1><form class="layui-form"><div class="layui-form-item"><input type="text" id="username" lay-verify="required" name="username" placeholder="请输入用户名"autocapitalize="off"class="layui-input"></div><div class="layui-form-item"><!--lay-verify表单校验 required (必填项) --><input type="password" id="password" lay-verify="required" name="password" placeholder="请输入密码"autocapitalize="off"class="layui-input"></div><div class="layui-form-item"><button class="layui-btn layui-btn-fluid" lay-submit lay-filter="login">登录</button></div></form></div><script src="/resources/layui/layui.all.js"></script><script>layui.form.on("submit(login)", function (formdata) {// data包含了当前表单的数据console.log(formdata)layui.$.ajax({url: "/check_login",data: formdata.field,type: "post",dataType: "json",success: function (json) {console.log(json);if (json.code === "0") {// layui.layer.msg("登陆成功");window.location.href = json.redirect_url;} else {layui.layer.msg(json.message);}}})return false;//submit提交事件返回true则表单提交,false则阻止表单提交});</script></body></html>
实现用户登录(后端)
创建User实体类
public class User {private Long userId; // user_idprivate String username;private String password;private Long employeeId;private Integer salt;public Long getUserId() {return userId;}public void setUserId(Long userId) {this.userId = userId;}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;}public Long getEmployeeId() {return employeeId;}public void setEmployeeId(Long employeeId) {this.employeeId = employeeId;}public Integer getSalt() {return salt;}public void setSalt(Integer salt) {this.salt = salt;}}
新建一个mapper用来写SQL 语句
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-////DTD Config 3.0//EN""/dtd/mybatis-3-mapper.dtd"><mapper namespace="usermapper"><!--按用户名获取用户对象--><select id="selectByUsername" parameterType="String" resultType="com.imooc.oa.entity.User">select *from sys_userwhere username = #{value}</select></mapper>
在mybatis-config.xml中声明新建的mapper
<mappers><mapper resource="mappers/user.xml"/></mappers>
用mvc的开发方式。创建UserDao
public class UserDao {/*** 按用户名查找表* @param username 用户名* @return User对象包含对应的用户信息, null则代表对象不存在*/public User selectByUsername(String username) {return (User) MybatisUtils.executeQuery(SqlSession ->SqlSession.selectOne("usermapper.selectByUsername", username));}}
创建一个UserService
public class UserService {private UserDao userDao = new UserDao();private RbacDao rbacDao = new RbacDao();/*** 根据前台输入进行校验* @param username 前台输入的用户名* @param password 前台输入的密码* @return 校验通过后, 包含对应数据的User实体类* @throws BussinessException L001-用户名不存在,L002-密码错误*/public User checkLogin(String username, String password) {// 按用户名查找用户User user = userDao.selectByUsername(username);if (user == null) {// 抛出用户不存在异常throw new BussinessException("L001", "用户名不存在");}String md5 = MD5Utils.md5Digest(password, user.getSalt());if (!md5.equals(user.getPassword())) {throw new BussinessException("L002", "密码错误");}return user;}public List<Node> selectNodeByUserId(Long userId) {return rbacDao.selectNodeByUserId(userId);}}
自定义一个异常
public class BussinessException extends RuntimeException {private String code;// 异常编码,异常的标识private String message; // 异常具体文本消息 public BussinessException(String code, String message) {super(code + ":" + message);this.code = code;this.message = message;}public String getCode() {return code;}public void setCode(String code) {this.code = code;}@Overridepublic String getMessage() {return message;}public void setMessage(String message) {this.message = message;}}
创建LoginServlet
@WebServlet(name = "LoginServlet", urlPatterns = "/check_login")public class LoginServlet extends HttpServlet {private UserService userService = new UserService();Logger logger = LoggerFactory.getLogger(LoginServlet.class);@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException {request.setCharacterEncoding("UTF-8");response.setContentType("text/html;charset=utf-8");// 接收用户输入String username = request.getParameter("username");String password = request.getParameter("password");Map<String, Object> result = new HashMap<>();try {// 调用业务逻辑User user = userService.checkLogin(username, password);HttpSession session = request.getSession();session.setAttribute("login_user", user);result.put("code", "0");result.put("message", "success");result.put("redirect_url", "/index");} catch (BussinessException ex) {logger.error(ex.getMessage(), ex);result.put("code", ex.getCode());result.put("message", ex.getMessage());} catch (Exception ex) {logger.error(ex.getMessage(), ex);result.put("code", ex.getClass().getSimpleName());result.put("message", ex.getMessage());}// 返回对应结果String json = JSON.toJSONString(result);response.getWriter().println(json);}@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException {}}
动态显示功能菜单
例如:获取编号为1的用户拥有哪些功能
通过用户找到角色,再通过角色找到节点编号,最后通过节点编号找到与之对应的其他信息
select DISTINCT n.*FROM sys_role_user ru,sys_role_node rn,sys_node nwhere ru.role_id = rn.role_idand user_id = 编号and rn.node_id = n.node_idORDER BY n.node_id
创建rbac.xml 同时记得注册登记
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-////DTD Config 3.0//EN""/dtd/mybatis-3-mapper.dtd"><mapper namespace="rbacmapper"><select id="selectNodeByUserId" resultType="com.imooc.oa.entity.Node" parameterType="Long">select DISTINCT n.*FROM sys_role_user ru,sys_role_node rn,sys_node nwhere ru.role_id = rn.role_idand user_id = #{value}and rn.node_id = n.node_idORDER BY n.node_id</select></mapper>
创建一个节点实体类。这里就不写了
创建一个RbacDao类。调用刚才的SQL
public class RbacDao {public List<Node> selectNodeByUserId(Long userId) {return (List<Node>) MybatisUtils.executeQuery(sqlSession -> sqlSession.selectList("rbacmapper.selectNodeByUserId", userId));}}
在UserService类中添加方法
public List<Node> selectNodeByUserId(Long userId) {return rbacDao.selectNodeByUserId(userId);}
如何在index界面中获取到用户的登录信息?
使用Session
根据用户登录的ID可以获取到该用户可用的模块列表(主要还是书写SQL语句)
@WebServlet(name = "IndexServlet", urlPatterns = "/index")public class IndexServlet extends HttpServlet {private UserService userService = new UserService();private EmployeeService employeeService = new EmployeeService();private DepartmentService departmentService = new DepartmentService();@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException {HttpSession session = request.getSession();// 得到当前登录用户对象User user = (User) session.getAttribute("login_user");// 获取当前登录的员工对象Employee employee = employeeService.selectById(user.getEmployeeId());// 获取员工对应的部门Department department = departmentService.selectById(employee.getDepartmentId());// 获取登录用户可用功能模板List<Node> nodeList = userService.selectNodeByUserId(user.getUserId());// 放入请求属性request.setAttribute("node_list", nodeList);session.setAttribute("current_employee", employee);session.setAttribute("current_department", department);// 请求派发至ftl进行展现request.getRequestDispatcher("/index.ftl").forward(request, response);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException {}}
XML配置下实现Mapper接口
查询登录的员工
创建对应的实体类
创建一个新的接口
public interface EmployeeDao {/*** 根据ID号查询对应的员工* @param employeeId 员工ID* @return 员工信息*/public Employee selectById(Long employeeId);}
创建xml
<!--namespace与包名一致--><mapper namespace="com.imooc.oa.dao.EmployeeDao"><!--id与方法名对应parameterType与方法参数类型对应resultType与方法返回类型对应--><select id="selectById" parameterType="Long" resultType="com.imooc.oa.entity.Employee">select *from adm_employeewhere employee_id = #{value}</select></mapper>
在配置文件中配置mapper
<mappers><mapper resource="mappers/employee.xml"/></mappers>
新增Service
public class EmployeeService {public Employee selectById(Long employeeId) {return (Employee) MybatisUtils.executeQuery(sqlSession -> {EmployeeDao employeeDao = sqlSession.getMapper(EmployeeDao.class);return employeeDao.selectById(employeeId);});}}
获取部门与这个流程一样
基于MD5算法对密码加密
MD5信息摘要算法广泛使用的密码散列函数
MD5可以产生出一个128位的散列值用于唯─标识源数据
项目中通常使用MD5作为敏感数据的加密算法
特点:
压缩性,MD5生成的摘要长度固定
抗修改,源数据哪怕有一个字节变化,MD5也会有巨大差异
不可逆,无法通过MD5反向推算源数据
Apache Commons CodeRc
Commons-Codec是Apache提供的编码/解码组件
通过Commons-Codec可轻易生成源数据的MD5摘要
MD5摘要方法: String md5 = DigestUtils.md5Hex(源数据)
在pop.xml中添加MD5摘要包
<dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId><version>1.14</version></dependency>
因为固定字符串MD5加密后都一样,所以我们要进行加盐处理。
public class MD5Utils {public static String md5Digest(String source) {return DigestUtils.md5Hex(source);}/*** 对源数据加盐混淆后生成MD5摘要* @param source 源数据* @param salt 盐值* @return MD5摘要*/public static String md5Digest(String source, Integer salt) {char[] ca = source.toCharArray();// 字符数组for (int i = 0; i < ca.length; i++) {ca[i] = (char) (ca[i] + salt);}String target = new String(ca);return DigestUtils.md5Hex(target);}public static void main(String[] args) {for (int i = 188; i <= 197; i++) {System.out.println(i);System.out.println(md5Digest("test", i));}}}
每个用户密码的盐值都不一样,在用户表中创建一个新字段用来存储对应的盐值。每次登录的时候获取用户输入的密码,然后将密码进行加盐MD5处理后判断是否与数据库中的密码相同。
注销操作
创建一个新的LogoutServlet
@WebServlet(name = "LogoutServlet", value = "/logout")public class LogoutServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException {// 清空Sessionrequest.getSession().invalidate();// 重定向到登录页response.sendRedirect("/login.html");}}