• 为bookStore添加权限【动态代理和注解】


    前言

    目前为止,我们已经学习了动态代理技术和注解技术了。于是我们想要为之前的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层方法中需要权限的地方添加注解
    
        @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是类似的】

    ServiceDao

    
        @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层调用该方法的时候就需要判断用户有没有该权限….

    要点总结

    1. 外界调用Service层的方法是代理调用invoke()方法,我们在invoke()方法可以对其进行增强!
    2. 在反射具体方法的时候,必须记得要给出相对应的参数!
    3. 在invoke()方法抛出的编译时期异常,java会自动转换成运行期异常进行抛出…
    4. 使用contains()方法时,就要重写该对象的hashCode()和equals()
    如果您觉得这篇文章帮助到了您,可以给作者一点鼓励



  • 相关阅读:
    .NET开发框架(六)-架构设计之IIS负载均衡(视频)
    Docker(二)-在Docker中部署Nginx实现负载均衡(视频教程)
    Docker(一)
    .NET Core跨平台部署于Docker(Centos)- 视频教程
    CentOS7 vsftp 安装与配置(视频教程)
    Hyper-V + CentOS7 安装教程(视频)
    火热的云原生到底是什么?一文了解云原生四要素!
    广州.NET微软技术俱乐部微信群各位技术大牛的blog
    .NET开发框架(五)-IIS上部署ASP.NET Core项目教程
    VMware 虚拟机网络配置
  • 原文地址:https://www.cnblogs.com/zhong-fucheng/p/7202995.html
Copyright © 2020-2023  润新知