用户模块
要登陆后才能购买, 因此我们先写购买模块.
设计实体
- private String id;
- private String username;
- private String password;
- private String email;
- private String cellphone;
- private String address;
- // 各种 settergetter
设计数据库表
- CREATE TABLE user(id VARCHAR(40) PRIMARY KEY, username VARCHAR(20) NOT NULL, cellphone VARCHAR(20) NOT NULL, address VARCHAR(40) NOT NULL, email VARCHAR(30), password VARCHAR(30) NOT NULL);##编写DAO##
- /**
- * 用户的登录注册模块
- * 1: 登陆
- * 2: 注册
- * 3: 根据 id 查找具体的用户
- */
- public class UserDaoImpl {
- public void register(User user) {
- QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
- String sql = "INSERT INTO user (id,username,cellphone,address,email,password) VALUES(?,?,?,?,?,?)";
- try {
- queryRunner.update(sql, new Object[] {
- user.getId(),
- user.getUsername(),
- user.getCellphone(),
- user.getAddress(),
- user.getEmail(),
- user.getPassword()
- });
- } catch(SQLException e) {
- throw new RuntimeException(e);
- }
- }
- public User login(String username, String password) {
- QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
- String sql = "SELECT * FROM user WHERE username = ? AND password=?";
- try {
- return (User) queryRunner.query(sql, new Object[] {
- username,
- password
- },
- new BeanHandler(User.class));
- } catch(SQLException e) {
- throw new RuntimeException(e);
- }
- }
- public User find(String id) {
- QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
- String sql = "SELECT * FROM user WHERE id=?";
- try {
- return (User) queryRunner.query(sql, id, new BeanHandler(User.class));
- } catch(SQLException e) {
- throw new RuntimeException(e);
- }
- }
- }
测试 DAO
- public class UserDemo {
- UserDaoImpl userDao = new UserDaoImpl();
- @Test
- public void add() {
- User user = new User();
- user.setId("1");
- user.setUsername("zhong");
- user.setPassword("123");
- user.setCellphone("10085");
- user.setAddress("广州萝岗");
- user.setEmail("40368324234234@QQ.com");
- userDao.register(user);
- }
- @Test
- public void find() {
- String id = "1";
- User user = userDao.find(id);
- System.out.println(user.getEmail());
- }
- @Test
- public void login() {
- String username = "zhong";
- String password = "123";
- User user = userDao.login(username, password);
- System.out.println(user.getAddress());
- }
- }
抽取 DAO
- public interface UserDao {
- void register(User user);
- User login(String username, String password);
- User find(String id);
- }
编写 Service
- private UserDao userDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.UserDaoImpl", UserDao.class);
- public void registerUser(User user) {
- userDao.register(user);
- }
- public User loginUser(String username,String password) {
- return userDao.login(username, password);
- }
- public User findUser(String id) {
- return userDao.find(id);
- }
前台样式
- head.jsp
- <div id="User">
用户名:<input type="text" name="username">
密码:<input type="password" name="password">
- <button name="login"> 登陆 </button>
- <button name="register"> 注册 </button>
- </div>
- head.css
- #body {
- position: relative;
- }
- #user {
- position: absolute;
- margin-top: 130px;
- margin-left: 1364px;
- }
效果:
实现登陆注册功能
当点击登陆按钮的时候, 把数据带过去给 Servlet, 让 Servlet 调用 BusinessService 方法, 实现登陆注册同理..... 因此, 我们需要用到 JavaScript 代码
- head.jsp
- <c:if test="${user==null}" >
- <div id="User">
用户名:<input type="text" id="username">
密码:<input type="password" id="password">
- <button name="login" onclick="login()"> 登陆 </button>
- <button name="register" onclick="register()"> 注册 </button>
- </div>
- </c:if>
- <c:if test="${user!=null}" >
- <div id="User">
欢迎您:${user.username} <a href="${pageContext.request.contextPath}/UserServlet?method=Logout"> 注销 </a>
- </div>
- </c:if>
javaScript 代码
- <script type="text/javascript">
- function login() {
- // 得到输入框的数据
- var username = document.getElementById("username").value;
- var password = document.getElementById("password").value;
- // 跳转到相对应的 Servlet 上
- window.location.href = "${pageContext.request.contextPath}/UserServlet?method=login&username=" + username + "&password=" + password;
- }
- function register() {
- // 跳转到注册页面
- window.location.href = "${pageContext.request.contextPath}/client/register.jsp";
- }
- </script>
- UserServlet
- String method = request.getParameter("method");
- BussinessServiceImpl service = new BussinessServiceImpl();
- if (method.equals("login")) {
- try {
- // 得到页面传递过来的数据
- String username = request.getParameter("username");
- String password = request.getParameter("password");
- User user = service.loginUser(username, password);
- request.getSession().setAttribute("user",user);
- request.getRequestDispatcher("/client/head.jsp").forward(request, response);
- } catch (Exception e) {
- request.setAttribute("message", "登陆失败了!");
- request.getRequestDispatcher("/message.jsp").forward(request, response);
- }
- } else if (method.equals("register")) {
- try {
- // 得到 JSP 传递过来的数据, 封装成 Bean 对象
- User user = WebUtils.request2Bean(request, User.class);
- user.setId(WebUtils.makeId());
- service.registerUser(user);
- request.setAttribute("message", "注册成功了!");
- } catch (Exception e) {
- e.printStackTrace();
- request.setAttribute("message", "注册失败了!");
- }
- request.getRequestDispatcher("/message.jsp").forward(request, response);
- } else if (method.equals("Logout")) {
- // 销毁 session
- request.getSession().invalidate();
- // 回到首页
- request.getRequestDispatcher("/client/head.jsp").forward(request, response);
- }
购买模块
在显示图书的时候, 顺便添加购买的超链接
<li><a href="#"> 购买 </a></li>
设计购物车实体
如果不清楚为什么这样设计, 可参考我之前的博文: blog.csdn.net/hon_3y/arti
Cart 实体
- public class Cart {
- private Map < String,
- CartItem > map = new HashMap < >();
- private double price;
- // 提供把商品添加到购物的功能
- public void addBook2Cart(Book book) {
- // 得到对应的购物项
- CartItem cartItem = map.get(book.getId());
- // 如果是 null, 说明购物车还没有该购物项
- if (cartItem == null) {
- cartItem = new CartItem();
- cartItem.setQuantity(1);
- cartItem.setBook(book);
- cartItem.setPrice(book.getPrice());
- // 把购物项加到购物车中
- map.put(book.getId(), cartItem);
- } else {
- // 如果购物车有该购物项了, 那么将购物项的数量 + 1
- cartItem.setQuantity(cartItem.getQuantity() + 1);
- }
- }
- // 购物车的价钱是购物项价钱的总和
- public double getPrice() {
- double totalPrice = 0;
- for (Map.Entry < String, CartItem > me: map.entrySet()) {
- CartItem cartItem = me.getValue();
- totalPrice += cartItem.getPrice();
- }
- return totalPrice;
- }
- public Map < String,
- CartItem > getMap() {
- return map;
- }
- public void setMap(Map < String, CartItem > map) {
- this.map = map;
- }
- public void setPrice(double price) {
- this.price = price;
- }
- }
设计购物项实体
- public class CartItem {
- private Book book;
- private double price;
- private int quantity;
- public double getPrice() {
- return this.book.getPrice() * this.quantity;
- }
- public void setPrice(double price) {
- this.price = price;
- }
- public Book getBook() {
- return book;
- }
- public void setBook(Book book) {
- this.book = book;
- }
- public int getQuantity() {
- return quantity;
- }
- public void setQuantity(int quantity) {
- this.quantity = quantity;
- }
- }
处理用户想要买的书籍 Servlet
- <li><a href="${pageContext.request
- .contextPath}/BuyServlet?book_id=${book.id}"> 购买 </a></li>
- BuyServlet
- BussinessServiceImpl service = new BussinessServiceImpl();
- // 先检查该用户是否登陆了
- User user = (User) request.getSession().getAttribute("user");
- if (user == null) {
- request.setAttribute("message", "您还没登陆, 请登陆了再来购买");
- request.getRequestDispatcher("/message.jsp").forward(request, response);
- return ;
- }
- // 如果登陆了...
- // 得到该用户的购物车
- Cart cart = (Cart) request.getSession().getAttribute("cart");
- if (cart == null) {
- cart = new Cart();
- request.getSession().setAttribute("cart", cart);
- }
- // 得到用户想买的书籍
- String book_id = request.getParameter("book_id");
- Book book = service.findBook(book_id);
- // 把书籍添加到购物车中
- service.buyBook(cart, book);
- request.setAttribute("message", "该商品已添加到购物车中");
- request.getRequestDispatcher("/message.jsp").forward(request,response);
提供显示购物车商品的 Servlet
- // 先判断该用户是否登陆了
- User user = (User) request.getSession().getAttribute("user");
- if (user == null) {
- request.setAttribute("message", "您还没有登陆呢!");
- request.getRequestDispatcher("/message.jsp").forward(request, response);
- return;
- }
- // 如果登陆了.....
- Cart cart = (Cart) request.getSession().getAttribute("cart");
- // 把该用户的购物车给 JSP 页面显示
- request.setAttribute("cart", cart);
- request.getRequestDispatcher("/client/listCart.jsp").forward(request, response);
显示购物车的 JSP 页面
<c:if test="${empty(cart.map)}">
您还没有购买过任何商品哦!!!
- </c:if>
- <table border="1px">
- <c:if test="${!empty(cart.map)}">
- <h1 > 您购物车下有如下的商品:</h1><br>
- <tr>
- <td > 书名:</td>
- <td > 作者:</td>
- <td > 数量:</td>
- <td > 价钱:</td>
- </tr>
- <c:forEach items="${cart.map}" var="cartItme">
- <tr>
- <td>${cartItme.value.book.name}</td>
- <td>${cartItme.value.book.author}</td>
- <td>${cartItme.value.quantity}</td>
- <td>${cartItme.value.price}</td>
- </tr>
- </c:forEach>
- </c:if>
- </table>
效果:
订单模块
在前台用户界面中, 当用户要把购物车付款时, 应该提供生成订单的超链接....
设计订单实体
订单应该包含 id, 收货人信息, 下单的时间, 订单的总价, 订单的状态有无发货.. 而不应该包含商品的信息的商品的信息用一个专门的订单项来表示
一个订单对应多个订单项, 这是一对多的关系!
- private String id;
- // 下单的时间日期状态
- private Date date;
- private double price;
- private boolean state;
- // 一个用户可以有多个订单, 把用户记住
- private String user_id;
- // 一个订单中有多个订单项
- private Set < OrderItem > items = new HashSet < >();
- // 各种的 setter 和 getter
设计订单项实体
- private String id;
- // 一本书对应多个订单项, 订单项一定是由书组成, 记住书
- private String book_id;
- private double price;
- private int quantity;
- // 各种的 setter 和 getter
设计数据库表
订单表
mysql 不能创建名为 order 的表, 后边加个 s 就可以
- CREATE TABLE orders (
- id VARCHAR(40) PRIMARY KEY,
- date DATE NOT NULL,
- user_id VARCHAR(40) NOT NULL,
- state BOOLEAN,
- price DOUBLE,
- CONSTRAINT user_id_FK FOREIGN KEY (user_id) REFERENCES user (id)
- );
订单项表:
- CREATE TABLE orderItem (
- id VARCHAR(40) PRIMARY KEY,
- price DOUBLE,
- quantity INT,
- order_id VARCHAR(40) ,
- book_id VARCHAR(40) ,
- CONSTRAINT order_id_FK FOREIGN KEY (order_id) REFERENCES orders (id),
- CONSTRAINT book_id_FK FOREIGN KEY (book_id) REFERENCES book (id)
- );
表之间的结构:
设计 Dao
- public class OrderDaoImpl implements zhongfucheng.dao.OrderDao {
- @Override
- public void addOrder(Order order) {
- QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
- String sql1 = "INSERT INTO orders(id,ordertime,user_id,state,price) VALUES(?,?,?,?,?)";
- try {
- // 订单的基本信息
- queryRunner.update(sql1, new Object[]{order.getId(), order.getOrdertime(), order.getUser_id(), order.isState(), order.getPrice()});
- // 订单项的信息
- String sql2 = "INSERT INTO orderItem(id,price,quantity,order_id,book_id) VALUES(?,?,?,?,?)";
- Set<OrderItem> items = order.getItems();
- for (OrderItem item : items) {
- queryRunner.update(sql2, new Object[]{item.getId(), item.getPrice(), item.getQuantity(), item.getOrder_id(), item.getBook_id()});
- }
- } catch (SQLException e) {
- throw new RuntimeException(e);
- }
- }
- @Override
- public Order findOrder(String id) {
- QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
- Order order;
- try {
- // 找出订单的基本信息
- String sql = "SELECT * FROM orders WHERE id=?";
- order = (Order) queryRunner.query(sql, new BeanHandler(Order.class), new Object[]{id});
- // 找出订单的所有订单项
- String sql2 = "SELECT * FROM orderItem WHERE order_id=?";
- List<OrderItem> list = (List<OrderItem>) queryRunner.query(sql2, new BeanListHandler(OrderItem.class), new Object[]{order.getId()});
- System.out.println("这是数据库拿到的 list 集合:"+list.size());
- // 将所有订单项装到订单里边
- order.getItems().addAll(list);
- System.out.println("这是数据库拿到的"+order.getItems().size());
- // 找出该订单是属于哪一个用户的
- String sql3 = "SELECT * FROM orders o,user u WHERE o.user_id=u.id AND o.id=?";
- User user = (User) queryRunner.query(sql3, new BeanHandler(User.class), new Object[]{order.getId()});
- order.setUser_id(user.getId());
- return order;
- } catch (SQLException e) {
- throw new RuntimeException(e);
- }
- }
- // 更新订单的状态
- public void updateState(String id) {
- QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
- String sql = "UPDATE orders SET state=? WHERE id=?";
- try {
- queryRunner.update(sql, new Object[]{true, id});
- } catch (SQLException e) {
- throw new RuntimeException(e);
- }
- }
- // 查看已经发货或没发货的订单信息
- public List<Order> getAllOrder(boolean state) {
- QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
- String sql = "SELECT * FROM orders WHERE state=?";
- try {
- return (List<Order>) queryRunner.query(sql, new BeanListHandler(Order.class), new Object[]{state});
- } catch (SQLException e) {
- throw new RuntimeException(e);
- }
- }
- // 通过用户的 id 查找用户的订单, 可能不止一个
- public List<Order> findUserOrder(String user_id) {
- QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
- String sql = "SELECT * FROM orders WHERE user_id=?";
- try {
- return List<Order> queryRunner.query(sql, new BeanHandler(Order.class), new Object[]{user_id});
- } catch (SQLException e) {
- throw new RuntimeException(e);
- }
- }
- }
二次更新
在编写 dao 的时候, 尤其是 Add 方法它是将所有数据都封装到 Order 对象上, 然后取出数据, 把数据插入到数据表中
其实, 我们的 Order 和 OrderItem 的操作可以分开 OrderItem 也可以另外编写一个 Dao, 那么我们在插入完 Order 对象之后, 得到 Order 对象返回的主键, 再调用 OrderItemDao 的方法来插入 OrderItem 的数据, 这样我觉得会让代码清晰一些
在 OrderItemDao 中接收的是一个 List, 因为我们一个订单会对应多个订单项
抽取成 DAO 接口
- public interface OrderDao {
- void addOrder(Order order);
- Order findOrder(String id);
- List<Order> getAllOrder(boolean state);
- void updateState(String user_id);
- List<Order> findUserOrder(String user_id);
- }
- BussinessService
- private OrderDao orderDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.OrderDaoImpl", OrderDao.class);
- public void createOrder(Cart cart, User user) {
- // 订单的基本信息
- String order_id = WebUtils.makeId();
- Order order = new Order();
- order.setId(order_id);
- order.setPrice(cart.getPrice());
- order.setOrdertime(new Date());
- order.setState(false);
- order.setUser_id(user.getId());
- // 订单项的基本信息
- // 得到每个购物项, 购物项就作为订单项
- for (Map.Entry<String, CartItem> me : cart.getMap().entrySet()) {
- OrderItem orderItem = new OrderItem();
- CartItem cartItem = me.getValue();
- orderItem.setId(WebUtils.makeId());
- orderItem.setPrice(cartItem.getPrice());
- orderItem.setBook_id(cartItem.getBook().getId());
- orderItem.setQuantity(cartItem.getQuantity());
- orderItem.setOrder_id(order_id);
- order.getItems().add(orderItem);
- }
- orderDao.addOrder(order);
- }
- public Order findOrder(String user_id) {
- return orderDao.findOrder(user_id);
- }
- public List<Order> getAllOrder(boolean state) {
- return orderDao.getAllOrder(state);
- }
- public void sendOutOrder(String id) {
- orderDao.updateState(id);
- }
- public List<Order> findUserOrder(String user_id) {
- return orderDao.findUserOrder(user_id);
- }
生成订单的 Servlet
- BussinessServiceImpl service = new BussinessServiceImpl();
- // 检查该用户的购物车是否有商品
- Cart cart = (Cart) request.getSession().getAttribute("cart");
- if (cart == null) {
- request.setAttribute("message", "您购物车没有商品, 无法生成订单");
- request.getRequestDispatcher("/message.jsp").forward(request, response);
- return;
- }
- // 如果有商品, 得到当前用户
- User user = (User) request.getSession().getAttribute("user");
- service.createOrder(cart, user);
- request.setAttribute("message", "订单已经生成了, 准备好钱来收货把");
- request.getRequestDispatcher("/message.jsp").forward(request, response);
- return;
用户查询自己的订单 Servlet
- <a href="${pageContext.request.contextPath}/LookOrder" target="body"> 查看订单 </a>
- BussinessServiceImpl service = new BussinessServiceImpl();
- // 检查该用户是否登陆了
- User user = (User) request.getSession().getAttribute("user");
- if (user == null) {
- request.setAttribute("message", "您还没登陆, 等您登陆了再来看把");
- request.getRequestDispatcher("/message.jsp").forward(request, response);
- return;
- }
- // 用户登陆了!
- Order order = service.findUserOrder(user.getId());
- // 交给相对应的 JSP 显示
- request.setAttribute("order", order);
- request.setAttribute("user",user);
- request.getRequestDispatcher("/client/listOrder.jsp").forward(request, response);
- return ;
显示订单数据的 JSP
<c:if test="${order==null}">
您还没有下过任何订单!!
- </c:if>
- <c:if test="${order!=null}">
- <table border="1px">
- <tr>
- <td > 下单人:</td>
- <td > 订单时间 </td>
- <td > 订单状态 </td>
- <td > 订单价钱 </td>
- </tr>
- <tr>
- <td>${user.username}</td>
- <td>${order.ordertime}</td>
- <td>${order.state==false?"未发货":"已发货"}</td>
- <td>${order.price}</td>
- </tr>
- </table>
- </c:if>
效果:
后台查询订单的状况 Servlet
- <a href="${pageContext.request.contextPath}/OrderServlet?state=false" target="body"> 待处理订单 </a><br>
- <a href="${pageContext.request.contextPath}/OrderServlet?state=true" target="body"> 已发货订单 </a><br>
- BussinessServiceImpl service = new BussinessServiceImpl();
- String state = request.getParameter("state");
- if (state.equals("true")) {
- List<Order> list = service.getAllOrder(true);
- request.setAttribute("list",list);
- } else if (state.equals("false")) {
- List<Order> list = service.getAllOrder(false);
- request.setAttribute("list", list);
- }
- request.getRequestDispatcher("/background/listOrder.jsp").forward(request, response);
显示订单状况的 JSP
<c:if test="${empty(list)}">
还没有任何订单哦!
- </c:if>
- <c:if test="${!empty(list)}">
- <table border="1px">
- <tr>
- <td > 下单人:</td>
- <td > 订单时间 </td>
- <td > 订单状态 </td>
- <td > 订单价钱 </td>
- <td > 操作 </td>
- </tr>
- <c:forEach items="${list}" var="order">
- <tr>
- <td>${order.user_id}</td>
- <td>${order.ordertime}</td>
- <td>${order.state==false?"未发货":"已发货"}</td>
- <td>${order.price}</td>
- <td>
- <a href="${pageContext.request.contextPath}/orderItemServlet?order_id=${order.id}"> 查看详细信息 </a>
- <a href="#"> 删除 </a>
- </td>
- </tr>
- </c:forEach>
- </table>
- </c:if>
查看具体订单的详细信息 Servlet
- BussinessServiceImpl service = new BussinessServiceImpl();
- // 得到用户想要查看详细信息的表单
- String order_id = request.getParameter("order_id");
- Order order = service.findOrder(order_id);
- // 将该 order 对象给相对应的 JSP 显示
- request.setAttribute("order", order);
- request.getRequestDispatcher("/background/listDetail.jsp").forward(request, response);
查看具体订单的详细信息 JSP
- <table border="1px">
- <tr>
- <td > 书籍的编号 </td>
- <td > 价钱 </td>
- <td > 数量 </td>
- <td > 操作 </td>
- </tr>
- <c:forEach items="${order.items}" var="item">
- <tr>
- <td>${item.book_id}</td>
- <td>${item.price}</td>
- <td>${item.quantity}</td>
- <td><a href="${pageContext.request.contextPath}/SendOutServlet?id=${order.id}"> 发货 </a></td>
- </tr>
- </c:forEach>
- </table>
处理发货的 Servlet##
- BussinessServiceImpl service = new BussinessServiceImpl();
- String id = request.getParameter("id");
- service.sendOutOrder(id);
- request.setAttribute("message", "已发货!");
- request.getRequestDispatcher("/message.jsp").forward(request, response);
- return;
效果:
添加权限控制
目前为止, 我们已经学习了动态代理技术和注解技术了于是我们想要为之前的 bookStore 项目添加权限控制.....
只有用户有权限的时候, 后台管理才可以进行相对应的操作.....
实现思路
之前我们做权限管理系统的时候, 是根据用户请求的 URI 来判断该链接是否需要权限的这次我们使用动态代理的技术和注解来判断: 用户调用该方法时, 检查该方法是否需要权限...
根据 MVC 模式, 我们在 web 层都是调用 service 层来实现功能的那么我们具体的思路是这样的:
web 层调用 service 层的时候, 得到的并不是 ServiceDao 对象, 而是我们的代理对象
在 service 层中的方法添加注解, 如果方法上有注解, 那么说明调用该方法需要权限...
当 web 层调用代理对象方法的时候, 代理对象会判断该方法是否需要权限, 再给出相对应的提示....
设计实体数据库表
上次我们做的权限管理系统是引入了角色这个概念的, 这次主要为了练习动态代理和注解技术, 就以简单为主, 不引入角色这个实体直接是用户和权限之间的关系了
Privilege 实体
- public class Privilege {
- private String id ;
- private String name;
- public String getId() {
- return id;
- }
- public void setId(String id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
数据库表
privilege 表
- CREATE TABLE privilege (
- id VARCHAR(40) PRIMARY KEY,
- name VARCHAR(40)
- );
privilege 和 user 是多对多的关系, 于是使用第三方表来维护他们的关系
user_privilege 表
- CREATE TABLE user_privilege (
- privilege_id VARCHAR(40),
- user_id VARCHAR(40),
- PRIMARY KEY (privilege_id, user_id),
- CONSTRAINT privilege_id_FK FOREIGN KEY (privilege_id) REFERENCES privilege(id),
- CONSTRAINT user_id_FK1 FOREIGN KEY (user_id) REFERENCES user(id)
- );
添加测试数据
为了方便, 直接添加数据了就不写详细的 DAO 了
在数据库中添加了两个权限
为 id 为 1 的 user 添加了两个权限
编写 DAO
后面在动态代理中, 我们需要检查该用户是否有权限... 那么就必须查找出该用户拥有的哪些权限再看看用户有没有相对应的权限
- // 查找用户的所有权限
- public List<Privilege> findUserPrivilege(String user_id) {
- QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
- String sql = "SELECT p.* FROM privilege p, user_privilege up WHERE p.id = up.privilege_id AND up.user_id = ?";
- try {
- return (List<Privilege>) queryRunner.query(sql, new Object[]{user_id}, new BeanListHandler(Privilege.class));
- } catch (SQLException e) {
- throw new RuntimeException(e);
- }
- }
抽取到接口上
List<Privilege> findUserPrivilege(String user_id);
注解模块
编写注解
- @Retention(RetentionPolicy.RUNTIME)
- public @interface permission {
- String value();
- }
在 Service 层方法中需要权限的地方添加注解 CategoryServiceImpl
- @permission("添加分类")
- /* 添加分类 */
- public void addCategory(Category category) {
- categoryDao.addCategory(category);
- }
- /* 查找分类 */
- public void findCategory(String id) {
- categoryDao.findCategory(id);
- }
- @permission("查找分类")
- /* 查看分类 */
- public List<Category> getAllCategory() {
- return categoryDao.getAllCategory();
- }
抽取 Service
把 Service 的方法抽取成 ServiceDao 在 Servlet 中, 也是通过 ServiceFactory 来得到 Service 的对象和 DaoFactory 是类似的
- CategoryService
- @permission("添加分类")
- /* 添加分类 */ void addCategory(Category category);
- /* 查找分类 */
- void findCategory(String id);
- @permission("查找分类")
- /* 查看分类 */ List<Category> getAllCategory();
- ServiceFactory
- public class ServiceDaoFactory {
- private static final ServiceDaoFactory factory = new ServiceDaoFactory();
- private ServiceDaoFactory() {
- }
- public static ServiceDaoFactory getInstance() {
- return factory;
- }
- // 需要判断该用户是否有权限
- public <T> T createDao(String className, Class<T> clazz, final User user) {
- System.out.println("添加分类进来了!");
- try {
- // 得到该类的类型
- final T t = (T) Class.forName(className).newInstance();
- // 返回一个动态代理对象出去
- return (T) Proxy.newProxyInstance(ServiceDaoFactory.class.getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() {
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, PrivilegeException {
- // 判断用户调用的是什么方法
- String methodName = method.getName();
- System.out.println(methodName);
- // 得到用户调用的真实方法, 注意参数!!!
- Method method1 = t.getClass().getMethod(methodName,method.getParameterTypes());
- // 查看方法上有没有注解
- permission permis = method1.getAnnotation(permission.class);
- // 如果注解为空, 那么表示该方法并不需要权限, 直接调用方法即可
- if (permis == null) {
- return method.invoke(t, args);
- }
- // 如果注解不为空, 得到注解上的权限
- String privilege = permis.value();
- // 设置权限后面通过它来判断用户的权限有没有自己
- Privilege p = new Privilege();
- p.setName(privilege);
- // 到这里的时候, 已经是需要权限了, 那么判断用户是否登陆了
- if (user == null) {
- // 这里抛出的异常是代理对象抛出的, sun 公司会自动转换成运行期异常抛出, 于是在 Servlet 上我们根据 getCause() 来判断是不是该异常, 从而做出相对应的提示
- throw new PrivilegeException("对不起请先登陆");
- }
- // 执行到这里用户已经登陆了, 判断用户有没有权限
- Method m = t.getClass().getMethod("findUserPrivilege", String.class);
- List<Privilege> list = (List<Privilege>) m.invoke(t, user.getId());
- // 看下权限集合中有没有包含方法需要的权限使用 contains 方法, 在 Privilege 对象中需要重写 hashCode 和 equals()
- if (!list.contains(p)) {
- // 这里抛出的异常是代理对象抛出的, sun 公司会自动转换成运行期异常抛出, 于是在 Servlet 上我们根据 getCause() 来判断是不是该异常, 从而做出相对应的提示
- throw new PrivilegeException("您没有权限, 请联系管理员!");
- }
- // 执行到这里的时候, 已经有权限了, 所以可以放行了
- return method.invoke(t, args);
- }
- });
- } catch (Exception e) {
- new RuntimeException(e);
- }
- return null;
- }
- }
- PrivilegeExcetption
当用户没有登陆或者没有权限的时候, 我们应该给用户一些友好的提示.... 于是我们自定义了 PrivilegeException
- public class PrivilegeException extends Exception {
- public PrivilegeException() {
- super();
- }
- public PrivilegeException(String message) {
- super(message);
- }
- public PrivilegeException(String message, Throwable cause) {
- super(message, cause);
- }
- public PrivilegeException(Throwable cause) {
- super(cause);
- }
- }
我们继承的是 Exception, 通过方法名抛出去但是我们是通过代理对象调用方法的, 于是 sun 公司的策略就是把它们转换成运行期异常抛出去
因此, 我们就在 Servlet 上得到异常, 再给出友好的提示
效果:
没有登陆的时候:
登陆了, 但是没有相对应的权限的时候
登陆了, 并且有权限
要点总结
该权限控制是十分优雅的, 只要我在 Service 层中添加一个注解... 那么当 web 层调用该方法的时候就需要判断用户有没有该权限....
外界调用 Service 层的方法是代理调用 invoke() 方法, 我们在 invoke() 方法可以对其进行增强!
invoke() 方法内部就是在查询调用该方法上有没有注解, 如果没有注解, 就可以直接调用如果有注解, 那么就得到注解的信息, 判断该用户有没有权限来访问这个方法
在反射具体方法的时候, 必须记得要给出相对应的参数!
在 invoke() 方法抛出的编译时期异常, java 会自动转换成运行期异常进行抛出...
使用 contains() 方法时, 就要重写该对象的 hashCode() 和 equals()
来源: https://juejin.im/post/5a955b755188257a836c5e0c