shiro(2)

六、自定义Realm

存在的问题:目前所有的 用户、角色、权限数据都在ini文件中,不利于管理。
实际项目开发中这些信息,应该在数据库中。所以需要为这3类信息建表

6.1 建表

用户表,角色表,权限表

create table t_user(
	id int primary key auto_increment,
    username varchar(20) not null unique,
    password varchar(100) not null
)engine=innodb default charset=utf8;

create table t_role(
	id int primary key auto_increment,
	role_name varchar(50) not null unique,
	create_time timestamp not null
)engine=innodb default charset=utf8;
  
create table t_permission(
	id int primary key auto_increment,
	permission_name varchar(50) not null unique,
	create_time timestamp
)engine=innodb default charset=utf8;
  
create table t_user_role(
	id int primary key auto_increment,
	user_id int references t_user(id),
	role_id int references t_role(id),
	unique(user_id,role_id)
)engine=innodb default charset=utf8;

create table t_role_permission(
	id int primary key auto_increment,
	permission_id int references t_permission(id),
	role_id int references t_role(id),
	unique(permission_id,role_id)
)engine=innodb default charset=utf8;

6.2 自定义Realm

Realm的职责是,为shiro加载 用户,角色,权限数据,以供shiro内部校验。

之前定义在ini中的数据,默认有IniRealm去加载。

现在库中的数据,需要自定义Realm去加载。

ops : 没必要在Realm中定义大量的查询数据的代码,可以为Realm定义好查询数据的DAO和Service。

6.2.1 父类

如下是Realm接口的所有子类,其中IniRealm是默认的Realm,负责加载shiro.ini中的[users]和[roles]信息,当shiro需要用户,角色,权限信息时,会通过IniRealm获得。

自定义realm有两个父类可以选择:

1> 如果realm只负责做身份认证 ,则可以继承:AuthenticatingRealm

2> 如果realm要负责身份认证和权限校验,则可以继承:AuthorizingRealm

6.2.2 定义Realm

public class MyRealm extends AuthorizingRealm {
   
    /**
     * 是否支持某种token
     * @param token
     * @return
     */
    @Override
    public boolean supports(AuthenticationToken token) {
   
        System.out.println("is support in realm1");
        if(token instanceof UsernamePasswordToken){
   
            return true;
        }
        return false;
    }

    /**
     * 当subject.login()时,shiro会调用Realm的此方法做用户信息的查询,然后做校验
     * 职责:通过用户传递来的用户名查询用户表,获得用户信息
     * 返回值:将查到的用户信息(用户名+密码)封装在AuthenticationInfo对象中返回
     * 异常:如果没有查到用户可抛出用户不存在异常;如果用户被锁定可抛出用户被锁异常;或其它自定义异常.
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
   
        //获得用户名
        String username = (String) token.getPrincipal();
        System.out.println("user:"+username+" is authenticating~~");
        UserService userService = 
            (UserService)ContextLoader.getCurrentWebApplicationContext().getBean("userService");
        //身份认证
        User user = userService.queryUser(username);
        System.out.println("user:"+user);
        /**
        如下代码可以省略,如果查询结果为空,直接返回null即可,
        shiro的后续流程已有类似判断逻辑,也会抛出UnknownAccountException
        if(user==null){//如果用户信息非法,则抛出异常
            System.out.println("用户不存在");
            throw new UnknownAccountException("username:"+username+"不存在");
        }
        **/
        //省略如上代码后,可以直接写:
        if(user == null){
   
            return null;
        }
        // 将 当前用户的认证信息存入 SimpleAuthenticationInfo 并返回
        // 注意此方法的本职工作就是查询用户的信息,所以查到后不用比对密码是否正确,那是shiro后续流程的职责。
        // 如果密码错误,shiro的后续流程中会抛出异常IncorrectCredentialsException
        return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),getName());
        /** 
        补充: 可以在user表中增加一列,用于存储用户是否被锁定,则查询的User对象中会有是否锁定的属性
             如果发现锁定则可以在此方法中抛出异常:LockedAccountException,
        **/
    }

    /**
     * 当触发权限或角色校验时:subject.isPermitted() / subject.checkPermission();
     *                       subject.hasRole() / subject.checkRole() 等。
     * 此时需要数据库中的 权限和角色数据,shiro会调用Realm的此方法来查询
     * 角色和权限信息存入SimpleAuthorizationInfo对象
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
   
        //获得username
        String username  = (String)principals.getPrimaryPrincipal();
        //新建SimpleAuthorizationInfo对象
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //查询当前用户的所有 "角色" 和 "权限"
        UserService userService = 
            (UserService)ContextLoader.getCurrentWebApplicationContext().getBean("userService");
        Set<String> roles = userService.queryRolesByUsername(username);
        Set<String> perms = userService.queryPermissionsByUsername(username);
        //“角色” 和 “权限” 存入 SimpleAuthorizationInfo对象
        info.setRoles(roles);
        info.setStringPermissions(perms);
        //返回SimpleAuthorizationInfo
        return info;
    }
}

6.3 配置Realm

shiro.ini中 配置自定义Realm

注意:[users] [roles] 两个部分不再需要

[main]
shiro.loginUrl = /login.jsp
shiro.unauthorizedUrl=/login.jsp
shiro.redirectUrl=/logout.jsp
shiro.postOnlyLogout = true
#注意:此处实在安装自定义Realm 指定realm
#声明Realm 名称 = Realm类路径
realm1 = com.zhj.realm.MyRealm
realm2 = com.zhj.realm.MyRealm2
#安装Reaml 关联到SecurityManager
securityManager.realms=$realm1,$realm2

[urls]
#照旧

web.xml配置不变

Shiro_自定义Realm后的 项目架构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U0ZI0KTH-1570717342900)(mdpic/shiro_spring架构.jpg)]

七 、加密

用户的密码是不允许明文存储的,因为一旦数据泄露,用户的隐私信息会完全暴露。

密码必须结果加密,生成密文,然后数据库中只存储用户的密码的密文。

在加密过程中需要使用到一些**“不可逆加密”**,如 md5,sha

所谓不可逆是指:

  • 加密函数A, 明文 “abc”, A(“abc”) = “密文”,不能通过 “密文” 反推出 “abc”,即使密文泄露密码仍然安全。

7.1 加密介绍

shiro支持hash(散列)加密,常见的如 md5, sha等

  • 基本加密过程

    md5(明文),sha(明文) 得到明文的密文,但明文可能比较简单导致密文容易被破解。
    
  • 加盐加密过程

    系统生成一个随机salt=“xxxxxx”, md5(明文+salt) ,sha(明文+salt),则提升了密文的复杂度。

  • 加盐多次迭代加密过程

    如果迭代次数为2,则加密2次: md5(明文+salt)=密文a , md5(密文a+salt)=最终密文

    ​ sha(明文+salt)=密文a , sha(密文a+salt)=最终密文

    ​ 则进一步提升了密文的复杂度,和被破解的难度。

加密过程中建议使用salt,并指定迭代次数,迭代次数的建议值1000+

实例代码:

String password
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值