ManagementDay04(一对一 多对一 多对多 一对多在hbm配置 配置shiro)

Apache Shiro是Java的一个安全框架,Shiro可以帮助我们完成:认证、授权、加密、会话管理、与Web集成、缓存等。
Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
Shiro不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过相应的接口注入给Shiro即可。
应用代码直接交互的对象是Subject,也就是说Shiro的对外API核心就是Subject;其每个API的含义:
Subject:主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者;
SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是Shiro的核心,它负责与后边介绍的其他组件进行交互,如果学习过SpringMVC,你可以把它看成DispatcherServlet前端控制器;
Realm:域,Shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。
1、应用代码通过Subject来进行认证和授权,而Subject又委托给SecurityManager;
2、我们需要给Shiro的SecurityManager注入Realm,从而让SecurityManager能得到合法的用户及其权限进行判断。
从以上也可以看出,Shiro不提供维护用户/权限,而是通过Realm让开发人员自己注入。
一、
1、Hibernate配置对象的关系
    权限架构,部门+用户+用户扩展信息+角色+模块
2、部门的自关联
    <many-to-one name="PARENT_ID" class="Dept" column="PARENT_ID"></many-to-one>
    name实体中的配置的对象属性
    class对应实体对象,写全路径my.domain.Dept有时候简写Dept,必须配置package 
    column对于数据库表中的外键
3、部门和用户,一对多
    <set name="users" cascade="all" inverse="true">
        <key column="DEPT_ID"/>
        <ont-to-many class="User"/>
    </set>
    Set标签,是个集合,在实体对象中配置Set<T> users
    name属性在实体中配置的集合的名称
    cascade级联,级联保护,删除(save-update,delete,delete-orphan,all,all-delete-orphan)
    inverse 交给另外一方来维护
    key 外键
    one-to-many.class 一对多具体调用的实例

cascade和inverse区别
Cascade主要用于级联操作(如:级联添加,删除)
Inverse主要用于控制权是否要反转,一般将控制权放在多方
    True代表控制权要反转,就交给多方维护,在一方加,效率会提高
    当删除部门时,记录删除部门下的所有用户
    Inverse="false"是默认值可以不配
    Update user set deptId=null where deptId=12;//解决父子关系
    Delete from user where deptId is null;//先干掉孩子
    Delete from dept where id =12;//自杀

    如果inverse="true" 代表多方维护关系
    Delete from user where deptId = 12;//自杀
    Delete from dept where id = 1;//父部门在子部门销毁后销毁

    False代表维护,是默认值

4、用户和部门,多对一,双向关联
    <many-to-one name="dept" class="Dept" column="DEPT_ID"/>
    Name配置关联的对象
    Class调用的实体对象
    Column 外键

    用户和用户扩展信息,一对一,单向关联
    <one-to-one name="userinfo" class="UserInfo" cascade="all"/>
    Name配置实体中声明的对象
    Class调用的实体对象
    Cascade级联

    将用户,用户扩展信息表中的主键生成策略:assigned
    在service中将用户,用户扩展的id设置为同一个取值 用主键一对一

5、用户和角色,多对多
    <set name="roles" table="ROLE_USER_P">
        <key column="USER_ID"></key>
        <many-to-many class="Role" column="ROLE_ID" order-by="ORDER_NO"></many-to-many>
    </set>
    Name在实体配置关联对象
    Table 多对多,配置中间表
    Key 外键
    many-to-many 多对多,class配置关联对象,column外键,order-by排序
    没有配置级联,inverse(删除角色不影响用户)
6、角色和   用户,多对多
    <set name="users" table="ROLE_USER_P">
        <key column="ROLE_ID"></key>
        <many-to-many class="User" column="USER_ID"></many-to-many>
    </set>

byte/short/int/char/varchar 执行效率
BYTE>SHORT>INT>CHAR>VARCHAR
    id使用varchar-》分布式
    Mysql数据库设计主键的时候使用int并带上mysql数据库自增

char和varchar的区别 
    char长度固定,执行效率更高,可能造成空间浪费
    varchar长度不固定 剩余的空间会进行回收,运行效率低

角色模块添加
    private Set<Module> modules = new HashSet<Module>(0);
还要在hibernate.cfg.xml中添加映射文件
    <mapping resource="classpath:my/domain/Module.hbm.xml"></mapping>

Shiro是apache的安全框架,用于解决系统的认证和授权问题,同时提供了会话管理,数据加密机制

需要关注
1、如何获得Subject
2、如何定义一个符合规定的Realm域(密码比较器的定义也要做)



Shiro使用步骤
1、导入jar包
maven
     <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-all</artifactId>
        <version>${shiro.version}</version>
     </dependency>

 web工程
    shiro-all
    shiro-core
    shiro-guice    
2、过滤器配置
    web.xml中一个filter 以一当十
    <!-- Shiro核心控制器 一定放在struts2过滤器之前 否则action无法创建-->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

3、产生代理类
    <!-- 使aspectJ注解起作用:自动为匹配的类生成代理对象 -->
    <!-- shiro安全框架产生代理子类的方式,使用cglib方式  -->
    <aop:aspectj-autoproxy proxy-target-class="true" />   

4、在applicationContext.xml文件中加载shiro配置文件
    <import resource="classpath:applicationContext-shiro.xml"></import>

5、编写密码比较器
    1、添加utils
    2、Md5Hash算法
        //高强度加密算法,不可逆
        public static String md5(String password, String salt){
            return new Md5Hash(password,salt,2).toString();
        }

        public static void main(String[] args) {
            System.out.println(new Md5Hash("123456","tony",2).toString());
        }
    3、密码比较器
    public class CustomCredentialsMatcher extends SimpleCredentialsMatcher{

        //密码比较的方法 token代表用户在界面输入的用户名和密码 info代表从数据库中得到的加密密码
        public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
            //向下转型
            UsernamePasswordToken upToken = (UsernamePasswordToken) token;

            //2、将用户在界面输入的原始密码加密 数组的toString返回数组的地址
            Object pwd = Encrypt.md5(new String(upToken.getPassword()), upToken.getUsername());

            //3、取出数据库中加密的密码
            Object dbPwd = info.getCredentials();
            return this.equals(pwd, dbPwd);
        }

    }
    配置
    <!-- 设置密码加密策略 md5hash -->
    <bean id="passwordMatcher" class="my.shiro.CustomCredentialsMatcher"/>

6、编写自定义Realm域
在UserService接口中加入新的方法
    private UserService userService;

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    //授权 当jsp页面出现shiro标签时,就会执行授权方法
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
        System.out.println("授权");
        User user = (User) pc.fromRealm(this.getName()).iterator().next();//根据realm的名字去找对应的realm

        Set<Role> roles = user.getRoles();//对象导航
        List<String> permissions = new ArrayList<String>();
        for(Role role:roles) {
            //遍历每个角色
            Set<Module> modules = role.getModules();//得到每个角色下的模块列表
            for(Module m :modules) {
                permissions.add(m.getName());
            }
        }
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermissions(permissions);//添加用户的权限
        return info;
    }

    //认证  token代表用户在界面上输入的用户名和密码
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("认证");

        //1、向下转型
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        //2、调用业务方法,实现根据用户名查询
        String hql = "from User where userName=?";
        List<User> list = userService.find(hql, User.class, new String[] {upToken.getUsername()});
        if(list!=null && list.size()>0) {
            User user = list.get(0);
            AuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
            return info;//此处如果返回,就会进入到密码比较器
        }

        return null;//就会出现异常
    }

Hibernate中实现数据检索的5种方式
    1、对象导航(通过关联级别的数据检索方式加载数据),前提:事先配好关联关系
    2、通过对象OID加载(get/load方法)
    3、通过HQL语句
    4、通过SQL语句
    5、通过QBC语句

7、配置AuthReal域
    <!-- 自定义权限认证 -->
    <bean id="authRealm" class="my.shiro.AuthRealm">
        <property name="userService" ref="userService"/>
        <!-- 自定义密码加密算法  -->
        <property name="credentialsMatcher" ref="passwordMatcher"/>
    </bean>     

8、登录操作 loginAction_login
    if(UtilFuns.isEmpty(username)) {
            return "login";
        }
        try {
            //1、得到subject
            Subject subject = SecurityUtils.getSubject();
            //2、调用登录方法
            UsernamePasswordToken token = new UsernamePasswordToken(username,password);
            subject.login(token);//当这行代码执行的时候,就会自动跳入到AuthRealm中认证方法

            //3、登录成功时,就从Shiro中取出用户的登录信息
            User user = (User) subject.getPrincipal();
            //4、将用户放入session域中
            session.put(SysConstant.CURRENT_USER_INFO, user);
        } catch (Exception e) {
            e.printStackTrace();
            request.put("errorInfo","用户名或密码错误");
            return "login";
        }

public class CustomCredentialsMatcher extends SimpleCredentialsMatcher{

    //密码比较的方法 token代表用户在界面输入的用户名和密码 info代表从数据库中得到的加密密码
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        //向下转型
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;

        //2、将用户在界面输入的原始密码加密 数组的toString返回数组的地址
        Object pwd = Encrypt.md5(new String(upToken.getPassword()), upToken.getUsername());

        //3、取出数据库中加密的密码
        Object dbPwd = info.getCredentials();
        return this.equals(pwd, dbPwd);
    }

}

15、授权
当jsp页面上出现Shiro标签时就会执行AuthRealm域中的授权方法
1、引入Shiro标签
<%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro" %>
2、使用shiro标签进行授权判断
    <shiro:hasPermission name="系统首页">
    <span id="topmenu" onclick="toModule('home');">系统首页</span><span id="tm_separator"></span>
    </shiro:hasPermission>
    <shiro:hasPermission name="货运管理">
        <span id="topmenu" onclick="toModule('cargo');">货运管理</span><span id="tm_separator"></span>
    </shiro:hasPermission>
    <shiro:hasPermission name="统计分析">
    <span id="topmenu" onclick="toModule('stat');">统计分析</span><span id="tm_separator"></span>
    </shiro:hasPermission>
    <shiro:hasPermission name="基础信息">
    <span id="topmenu" onclick="toModule('baseinfo');">基础信息</span><span id="tm_separator"></span>
    </shiro:hasPermission>
    <shiro:hasPermission name="系统管理">
    <span id="topmenu" onclick="toModule('sysadmin');">系统管理</span>
    </shiro:hasPermission>
    <shiro:hasPermission name="流程管理">
    <span id="topmenu" onclick="toModule('activiti');">流程管理</span>
    </shiro:hasPermission>

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
ehcache-shiro.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" name="shiroCache">

    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            />
</ehcache>

猜你喜欢

转载自blog.csdn.net/civilizationv/article/details/80260635