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

来源:互联网 发布:c语言的编程环境 编辑:程序博客网 时间:2024/05/15 12:40

前言

目前为止,我们已经学习了动态代理技术和注解技术了。于是我们想要为之前的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层调用该方法的时候就需要判断用户有没有该权限….

要点总结

  1. 外界调用Service层的方法是代理调用invoke()方法,我们在invoke()方法可以对其进行增强!
  2. 在反射具体方法的时候,必须记得要给出相对应的参数!
  3. 在invoke()方法抛出的编译时期异常,java会自动转换成运行期异常进行抛出…
  4. 使用contains()方法时,就要重写该对象的hashCode()和equals()
1 0