Shiro系列-Shiro中Realm如何使用

导语
  之前的分享中,了解到了用户身份认证,在说用户认证的时候提到了一个概念就是Realm,在之前的入门分享中提到了,Realm其实就是一个安全数据源,那么怎么样使用这个安全数据源呢?下面就来一起研究一下

Realm概念

  Realm 域,Shiro从Realm获取安全数据(例如用户、角色、权限等)。之前提到SecurityManager需要进行身份验证就必须从Realm中获取到一个合法的用户身份,从而比较用户身份是否合法,同时在SecurityManager获取身份的同时Realm也需要维护一套用户身份用来判断用户是否能执行某项操作。可以把Realm看作是一个DataSource。也就是上面提到的安全数据源。

  在之前的例子中的ini配置文件就可以看做是一个安全数据源

Realm接口源码

在这里插入图片描述

public interface Realm {

	//返回一个唯一的Realm名字
    String getName();
	//判断此Realm是否支持此Token
    boolean supports(AuthenticationToken token);
    //根据Token获取认证信息
    AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;
}

  从上面代码中看到Realm最为重要的一个事情就是根据一个Token获取的认证的信息。

Realm如何使用

  在之前有简单的提到,Realm 使用的时候可以是单个也可以是多个。下面就来分别Realm的不同使用方式。

单个Realm配置

1、自定Realm的实现com.nihui.shiro.realm.MyRealm类中
public class MyRealm implements Realm {

    public String getName() {
        return "myrealm";
    }
    public boolean supports(AuthenticationToken token) {
        //仅仅支持一个UsernamePaasswordToke
        return token instanceof UsernamePasswordToken;
    }
    public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //获取到用户名和密码
        String username = (String) token.getPrincipal();
        String password = new String((char[]) token.getCredentials());
        if (!"nihui".equals(username)){
            //用户名错误
            throw new UnknownAccountException();
        }
        if (!"123".equals(password)){
            //密码错误
            throw new IncorrectCredentialsException();
        }
        //如果身份认证验证成功,返回一个AuthenticationInfo
        return new SimpleAuthenticationInfo(username,password,getName());
    }
}
2、ini 配置文件指定自定义的Realm
# 声明一个realm
myRealm=com.nihui.shiro.realm.MyRealm
# 指定SecurityManager的Realm实现
securityManager.realms=$myRealm
3、测试效果
public class SRealeTest {
    public static void main(String[] args) {
        //1、获取SecurityManager工厂,
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-realm.ini");
        //2、得到一个SecurityManager实例,绑定到SecurityUtils
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);

        //得到Subject 以及用户名密码的身份验证Token
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("nihui","123");
        // 验证登陆

        try {
            subject.login(token);
        }catch (AuthenticationException e){
            //身份认证失败
        }
        System.out.println(subject.isAuthenticated()); //true表示用户已经登陆

        //退出操作
        subject.logout();

    }
}

多个Realm配置

  多个Realm只需要将配置文件,改为如下的内容分别实现两个Realm就可以了。

# 声明realm
# 声明realm
myRealm1=com.nihui.shiro.realm.realmconfig.MyRealm1
myRealm2=com.nihui.shiro.realm.realmconfig.MyRealm2
# 指定SecurityManager的Realm实现
securityManager.realms=$myRealm1,$myRealm2

  按照SecurityManager会按照realms指定的顺序进行身份认证,按照上面配置文件中指定的顺序,来进行Realm的认证,如果删除了最后指定的securityManager.realms,那么会按照Realm声明的顺序来进行匹配。下面 就来测试一下

Realm1 规则用户名admin,密码123

public class MyRealm1 implements Realm {

    public String getName() {
        return "myrealm1";
    }
    public boolean supports(AuthenticationToken token) {
        //仅仅支持一个UsernamePaasswordToke
        return token instanceof UsernamePasswordToken;
    }
    public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //获取到用户名和密码
        String username = (String) token.getPrincipal();
        String password = new String((char[]) token.getCredentials());
        if (!"admin".equals(username)){
            //用户名错误
            throw new UnknownAccountException();
        }
        if (!"123".equals(password)){
            //密码错误
            throw new IncorrectCredentialsException();
        }
        //如果身份认证验证成功,返回一个AuthenticationInfo
        return new SimpleAuthenticationInfo(username,password,getName());
    }
}

Realm2 规则用户名nihui,密码1234

public class MyRealm2 implements Realm {

    public String getName() {
        return "myrealm2";
    }
    public boolean supports(AuthenticationToken token) {
        //仅仅支持一个UsernamePaasswordToke
        return token instanceof UsernamePasswordToken;
    }
    public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //获取到用户名和密码
        String username = (String) token.getPrincipal();
        String password = new String((char[]) token.getCredentials());
        if (!"nihui".equals(username)){
            //用户名错误
            throw new UnknownAccountException();
        }
        if (!"1234".equals(password)){
            //密码错误
            throw new IncorrectCredentialsException();
        }
        //如果身份认证验证成功,返回一个AuthenticationInfo
        return new SimpleAuthenticationInfo(username,password,getName());
    }
}

测试类

public class MultiRealmTest {
    public static void main(String[] args) {
        //1、获取SecurityManager工厂,
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-multi-realm.ini");
        //2、得到一个SecurityManager实例,绑定到SecurityUtils
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);

        //得到Subject 以及用户名密码的身份验证Token
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("nihui","1234");
//        UsernamePasswordToken token = new UsernamePasswordToken("admin","123");
        // 验证登陆

        try {
            subject.login(token);
        }catch (AuthenticationException e){
            //身份认证失败
        }
        System.out.println(subject.isAuthenticated()); //true表示用户已经登陆

        //退出操作
        subject.logout();

    }
}

  这个时候如果将配置文件改为如下,会发现它是按照默认顺序进行匹配的并且两个内容都进行了匹配。

# 声明realm
myRealm1=com.nihui.shiro.realm.realmconfig.MyRealm1
myRealm2=com.nihui.shiro.realm.realmconfig.MyRealm2
# 指定SecurityManager的Realm实现
#securityManager.realms=$myRealm1,$myRealm2

  如果将配置文件改为如下的内容效果如何呢?这时候就会发现只有一个nihui的规则被进行了匹配,没有指定的admin的规则则没有被匹配到。

# 声明realm
myRealm1=com.nihui.shiro.realm.realmconfig.MyRealm1
myRealm2=com.nihui.shiro.realm.realmconfig.MyRealm2
# 指定SecurityManager的Realm实现
securityManager.realms=$myRealm1

Shiro默认提供的Realm

  在org.apache.shiro.realm包中提供了默认的一些Realm实现,主要有如下一些内容
在这里插入图片描述
  一般情况下在使用扩展的时候只需要继承AuthorizingRealm(授权)就可以了,AuthorizingRealm继承了AuthenticatingRealm(身份验证),同时也间接的继承了CachingRealm(带有缓存的实现)。在上面类中比较重要的几个默认实现。

在实际中的使用

org.apache.shiro.realm.text.IniRealm

  [users]部分指定了用户名密码以及其角色,[roles]部分指定角色信息具体配置可以参考官网IniRealm配置

[users]
nihui=123
test=123
admin=123

org.apache.shiro.realm.text.PropertiesRealm

  通过配置文件的形式来指定。

private static final int DEFAULT_RELOAD_INTERVAL_SECONDS = 10;
private static final String USERNAME_PREFIX = "user.";
private static final String ROLENAME_PREFIX = "role.";
private static final String DEFAULT_RESOURCE_PATH = "classpath:shiro-users.properties";

org.apache.shiro.realm.jdbc.JdbcRealm

  通过SQL查询响应的信息,例如从用户表中查询用户信息,从角色表中查询角色信息。

   /**
     * The default query used to retrieve account data for the user.
     */
    protected static final String DEFAULT_AUTHENTICATION_QUERY = "select password from users where username = ?";
    
    /**
     * The default query used to retrieve account data for the user when {@link #saltStyle} is COLUMN.
     */
    protected static final String DEFAULT_SALTED_AUTHENTICATION_QUERY = "select password, password_salt from users where username = ?";

    /**
     * The default query used to retrieve the roles that apply to a user.
     */
    protected static final String DEFAULT_USER_ROLES_QUERY = "select role_name from user_roles where username = ?";

    /**
     * The default query used to retrieve permissions that apply to a particular role.
     */
    protected static final String DEFAULT_PERMISSIONS_QUERY = "select permission from roles_permissions where role_name = ?";

自定义Realm实现

  通过继承AuthorizingRealm类来实现自定义的Realm实现。


public class ShiroRealm extends AuthorizingRealm {
    @Autowired
    private AdminService adminService;

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //在实际项目中,这里可以根据实际情况做缓存操作。如果没有缓存操作,Shiro有自己的时间隔离机制,2分钟不会重复执行该方法。
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        //获取到用户账号通过用户账号获取到用户信息
        String account = (String) token.getPrincipal();

//        UserAdmin userInfo = adminService.findByUsername(account);
        Admin userInfo  =adminService.findByUsername(account);
        if (userInfo==null){
            throw new UnknownAccountException();
        }
        if ("0".equals(userInfo.getStatus().toString())){
            throw new LockedAccountException();
        }

        SecurityUtils.getSubject().getSession().setAttribute("user",userInfo);

        ByteSource credentialsSalt = ByteSource.Util.bytes(userInfo.getPublic_salt());
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                userInfo,
                userInfo.getPassword(),
                credentialsSalt,
                getName());

        return authenticationInfo;
    }

    //这方法是用来做权限认证的方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }
}

总结

  通过上面的内容了解到了Realm作为关键数据节点在Shiro中的存在,并且介绍了关于Realm的类继承关系,了解了Realm的存在价值,如何使用Realm。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nihui123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值