为了账号安全,请及时绑定邮箱和手机立即绑定

图书管理系统【用户、购买、订单模块、添加权限】(3)

标签:
Java
添加权限控制

目前为止,我们已经学习了动态代理技术和注解技术了。于是我们想要为之前的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. invoke()方法内部就是在查询调用该方法上有没有注解,如果没有注解,就可以直接调用。如果有注解,那么就得到注解的信息,判断该用户有没有权限来访问这个方法
  3. 在反射具体方法的时候,必须记得要给出相对应的参数!
  4. 在invoke()方法抛出的编译时期异常,java会自动转换成运行期异常进行抛出...
  5. 使用contains()方法时,就要重写该对象的hashCode()和equals()

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
1.5万
获赞与收藏
6488

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消