shiro笔记(二)

使用shiro进行认证通常不会将用户信息直接写在ini文件中,目前假设从某数据源中获取用户信息,密码暂不加密一切从简。获取数据的这一步操作就要交给realm(域)来进行。

而认证是由securityManager指派认证器来完成,所以需要将自定义的realm交给securityManager。

首先,实现一个自定义的realm,没有继承AuthorizingRealm,而是直接实现realm接口(因为教程上是这么做的,这个应该是按照需求而决定)。

public class SelfRealm implements Realm {
    @Override
    public String getName() {
        return "SelfRealm"; //realm的名字
    }

    @Override
    public boolean supports(AuthenticationToken authenticationToken) {
        return authenticationToken instanceof UsernamePasswordToken; //支持的token类型
    }

    @Override
    public AuthenticationInfo getAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String username = (String) authenticationToken.getPrincipal();
        String password = new String((char[])authenticationToken.getCredentials());
        if(!"joker".equals(username)) { //模拟从数据源获取用户名
            throw new UnknownAccountException();
        }
        if(!"shiro".equals(password)) { //模拟从数据源获取密码
            throw new IncorrectCredentialsException();
        }
        return new SimpleAuthenticationInfo(username, password, getName());
    }
}

测试类

public class Test {

    @org.junit.Test
    public void test() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //创建核心管理器
        securityManager.setRealm(new SelfRealm()); //将自定义的realm交给核心管理器
        SecurityUtils.setSecurityManager(securityManager);
        Subject subject = SecurityUtils.getSubject(); //获取主体
        UsernamePasswordToken token = new UsernamePasswordToken("joker", "shiro"); //创建token
        subject.login(token); //登录
    }
}

进行认证时会调用getAuthenticationInfo方法,UsernamePasswordToken是AuthenticationToken的实现类,subject.isAuthenticated()可以获取认证的状态,true为成功,false为失败。

以上为单realm配置,在shiro还支持多realm配置,可以配置在ini文件中。

#声明realm  
FirstRealm=全类名
SecondRealm=全类名 
#配置securityManager的realms 
securityManager.realms=$FirstRealm,$SecondRealm

会按照realm的配置顺序进行认证,如果没有再ini文件中配置,则会按照声明顺序进行认证,当在ini文件中配置了realm时,没有写入配置的realm会被忽略。

那是否可以不通过配置文件来设置realm呢?

接下来我自己动手实践了一下,首先对自定义realm进行改造。

public class SelfRealm implements Realm {

    Integer id;

    public SelfRealm(Integer id) { //创建实例对象时,为对象设置一个id
        this.id = id;
    }

    @Override
    public String getName() {
        return "SelfRealm";
    }

    @Override
    public boolean supports(AuthenticationToken authenticationToken) {
        return authenticationToken instanceof UsernamePasswordToken;
    }

    @Override
    public AuthenticationInfo getAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println(id); //在执行认证操作时,将id打印到控制台
        String username = (String) authenticationToken.getPrincipal();
        String password = new String((char[])authenticationToken.getCredentials());
        if(!"joker".equals(username)) {
            throw new UnknownAccountException(); 
        }
        if(!"shiro".equals(password)) {
            throw new IncorrectCredentialsException(); 
        }
        return new SimpleAuthenticationInfo(username, password, getName());
    }
}

然后将两个仅仅是id不同的realm交给securityManager,观察是否会按照设置的先后顺序进行执行?是否两个realm都会被执行?

测试方法部分改造如下:

securityManager.setRealm(new SelfRealm(0));
securityManager.setRealm(new SelfRealm(2));

但是结果是id为2的realm被执行,而id为0的realm对象没有被执行,看来应该是被id为2的覆盖掉了。

接下来我进入了源码看了一下,发现了setRealms方法,setRealms(Collection<Realm> realms),可以传入集合参数,这波应该是稳了。

public class Test {

    @org.junit.Test
    public void test() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        List<Realm> selfRealmList = new ArrayList<>();
        selfRealmList.add(new SelfRealm(0));
        selfRealmList.add(new SelfRealm(2));
        securityManager.setRealms(selfRealmList);
        SecurityUtils.setSecurityManager(securityManager);
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("joker", "shiro");
        try {
            subject.login(token);
        } catch (Exception e) {}
        System.out.println(subject.isAuthenticated());
    }
}

果然这次两个realm都被执行,并且是按照添加进集合的顺序执行的。

接下来教程中提到了一些关于自定义realm的常用做法和shiro提供的默认realm:

一般继承AuthorizingRealm,它继承了AuthenticatingRealm(身份验证),而且也间接继承了CachingRealm(带有缓存实现)。

主要默认实现有:IniRealm、PropertiesRealm、JdbcRealm。

接下在教程中介绍了一下JdbcRealm的使用,以及三种认证策略,没有找到用代码设置认证策略的方式,所以此处暂不记录。

posted @ 2020-10-29 14:21  无心大魔王  阅读(71)  评论(0编辑  收藏  举报