Springboot Shiro 多Realm 认证和授权

Shiro 多Realm 认证和授权

一本正经教学时间

新人博主,求点赞求关注

1、多Realm

先分别编写好多个Realm各种认证和授权的规则 这里是StaffRealm.java 和 StudentRealm.java

//StaffRealm
public class StaffRealm extends AuthorizingRealm {
    @Autowired
    StaffRoleServiceImpl staffRoleService;
    @Autowired
    RolePermissionServiceImpl permissionService;
    @Autowired
    StaffServiceImpl staffService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addRole(Constants.StaffRole);
        Subject subject = SecurityUtils.getSubject();
        Staff staff = (Staff) subject.getPrincipal();
        List<Role> roles = staffRoleService.queryRoleByStaffId(staff.getId());
        for(Role role:roles){
            info.addRole(role.getName());
            List<String> permissions = permissionService.queryStringPermissionByRoleId(role.getId());
            info.addStringPermissions(permissions);
        }
        return info;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken userToken = (UsernamePasswordToken)token;
        try{
            int id = Integer.parseInt(userToken.getUsername());
            Staff staff = staffService.getById(id);
            if(staff==null){
                return null;
            }
            return new SimpleAuthenticationInfo(staff,staff.getPassword(),"StaffRealm");
        }catch (NumberFormatException e){
            return null;
        }
    }
}
//StudentRealm
public class StudentRealm extends AuthorizingRealm {
    @Autowired
    StudentService studentService;
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addRole(Constants.StudentRole);
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken userToken = (UsernamePasswordToken)token;
        try{
            String id = userToken.getUsername();
            Student student = studentService.getById(id);
            if(student==null){
                return null;
            }
            return new SimpleAuthenticationInfo(student,student.getPassword(),"StudentRealm");
        }catch (NumberFormatException e){
            return null;
        }
    }
}

将这两个Realm注入到ShiroConfig的SecurityManager中,

@Bean(name="SecurityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("MySessionManager")DefaultWebSessionManager sessionManager){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    //================================================//
        List<Realm> realms = new ArrayList<>();
        realms.add(staffRealm());
        realms.add(studentRealm());
        securityManager.setRealms(realms);//设置多Realm
    //================================================//
        return securityManager;
    }
@Bean
public StaffRealm staffRealm(){
    return new StaffRealm();
}
@Bean
public StudentRealm studentRealm(){
    return new StudentRealm();
}

2、编写一个增强的UsernamePasswordToken

主要是为了在登录使用token时,能区分以那种方式认证,该token也会在授权时被使用

public class ShiroUsernamePasswordToken extends UsernamePasswordToken {

    /*
     * 当前登录用户类型
     */
    private String userType;

    public String getUserType() {
        return userType;
    }

    public void setUserType(String userType) {
        this.userType = userType;
    }

    public ShiroUsernamePasswordToken(String username, String password, String userType) {
        super(username, password);
        this.userType = userType;
    }
}

3、重写ModularRealmAuthorizer和ModularRealmAuthenticator

ModularRealmAuthenticator:模块认证器 区分不同的Realm进行不同需求的身份验证

ModularRealmAuthorizer:模块授权器 区分不同的Realm进行不同需求的角色权限授权

UserModularRealmAuthenticator.java

我们重写ModularRealmAuthenticator中的doAuthenticate,以达到区分不同Realm认证的目的

public class UserModularRealmAuthenticator extends ModularRealmAuthenticator {

    private Logger logger = LoggerFactory.getLogger(UserModularRealmAuthenticator.class);

    /**
     * 根据用户类型判断使用哪个Realm
     */
    @Override
    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)
            throws AuthenticationException {
        super.assertRealmsConfigured();
        // 强制转换回自定义的CustomizedToken
        ShiroUsernamePasswordToken token = (ShiroUsernamePasswordToken) authenticationToken;
        // 登录类型
        String userType = token.getUserType();
        // 所有Realm
        Collection<Realm> realms = getRealms();
        // 登录类型对应的所有Realm
        Collection<Realm> typeRealms = new ArrayList<>();
        for (Realm realm : realms) {
            //根据登录类型和Realm的名称进行匹配区分
            if(realm.getName().contains(userType)){
                typeRealms.add(realm);
            }
        }
        // 判断是单Realm还是多Realm,有多个Realm就会使用所有配置的Realm。 只有一个的时候,就直接使用当前的Realm。
        if (typeRealms.size() == 1) {
            logger.info("doSingleRealmAuthentication() execute ");
            return doSingleRealmAuthentication(typeRealms.iterator().next(), token);
        } else {
            logger.info("doMultiRealmAuthentication() execute ");
            return doMultiRealmAuthentication(typeRealms, token);
        }
    }
}

UserModularRealmAuthorizer.java

我们重写ModularRealmAuthorizer中的isPermitted和hasRole方法,达到用不同的Realm进行不同的授权规则的目的

public class UserModularRealmAuthorizer extends ModularRealmAuthorizer {
    @Override
    public boolean isPermitted(PrincipalCollection principals, String permission) {
        assertRealmsConfigured();
        // 所有Realm
        Collection<Realm> realms = getRealms();
        HashMap<String, Realm> realmHashMap = new HashMap<>(realms.size());
        for (Realm realm : realms) {
            if (realm.getName().contains(Constants.UserType.STAFF.type)) {
                realmHashMap.put("StaffRealm", realm);
            } else if (realm.getName().contains(Constants.UserType.STUDENT.type)) {
                realmHashMap.put("StudentRealm", realm);
            }
        }

        Set<String> realmNames = principals.getRealmNames();
        if (realmNames != null) {
            String realmName = null;
            Iterator it = realmNames.iterator();
            while (it.hasNext()) {
                realmName = ConvertUtils.convert(it.next());
                if (realmName.contains(Constants.UserType.STAFF.type)) {
                    return ((StaffRealm) realmHashMap.get("StaffRealm")).isPermitted(principals, permission);
                } else if (realmName.contains(Constants.UserType.STUDENT.type)) {
                    return ((StudentRealm) realmHashMap.get("StudentRealm")).isPermitted(principals, permission);
                }
                break;
            }
        }
        return false;
    }

    @Override
    public boolean hasRole(PrincipalCollection principals, String roleIdentifier) {
        assertRealmsConfigured();
        // 所有Realm
        Collection<Realm> realms = getRealms();
        HashMap<String, Realm> realmHashMap = new HashMap<>(realms.size());
        for (Realm realm : realms) {
            if (realm.getName().contains(Constants.UserType.STAFF.type)) {
                realmHashMap.put("StaffRealm", realm);
            } else if (realm.getName().contains(Constants.UserType.STUDENT.type)) {
                realmHashMap.put("StudentRealm", realm);
            }
        }

        Set<String> realmNames = principals.getRealmNames();
        if (realmNames != null) {
            String realmName = null;
            Iterator it = realmNames.iterator();
            while (it.hasNext()) {
                realmName = ConvertUtils.convert(it.next());
                if (realmName.contains(Constants.UserType.STAFF.type)) {
                    return ((StaffRealm) realmHashMap.get("StaffRealm")).hasRole(principals, roleIdentifier);
                } else if (realmName.contains(Constants.UserType.STUDENT.type)) {
                    return ((StudentRealm) realmHashMap.get("StudentRealm")).hasRole(principals, roleIdentifier);
                }
                break;
            }
        }
        return false;
    }
}

4、注入重写的ModularRealmAuthorizer和ModularRealmAuthenticator

@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(){
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    List<Realm> realms = new ArrayList<>();
    realms.add(staffRealm());
    realms.add(studentRealm());
    securityManager.setAuthenticator(modularRealmAuthenticator());
    securityManager.setAuthorizer(modularRealmAuthorizer());
    securityManager.setRealms(realms);

    return securityManager;
}

@Bean
public UserModularRealmAuthorizer modularRealmAuthorizer(){
    UserModularRealmAuthorizer modularRealmAuthorizer = new UserModularRealmAuthorizer();
    return modularRealmAuthorizer;
}
@Bean
public ModularRealmAuthenticator modularRealmAuthenticator() {
    UserModularRealmAuthenticator modularRealmAuthenticator = new UserModularRealmAuthenticator();
    modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
    return modularRealmAuthenticator;
}

@Bean
public StaffRealm staffRealm(){
    return new StaffRealm();
}
@Bean
public StudentRealm studentRealm(){
    return new StudentRealm();
}

5、Controller的使用

@PostMapping("/login")
public String login(String username, String password, Model model){
    Subject subject = SecurityUtils.getSubject();
    ShiroUsernamePasswordToken token = new ShiroUsernamePasswordToken(username,password,Constants.UserType.STUDENT.type);//设置Realm的type "StudentRealm"
    try{
        subject.login(token);
        Student student = (Student)subject.getPrincipal();
        Session session = subject.getSession();
        session.setAttribute(Constants.StudentNameSession,student.getName());
        return "redirect:/";
    }catch (UnknownAccountException e){
        model.addAttribute("msg","用户名或密码错误");
        return "student/login";
    }catch (IncorrectCredentialsException e){
        model.addAttribute("msg","用户名或密码错误");
        return "student/login";
    }
}

罗里吧嗦踩坑时间

在网上找了资料 有使用到 https://blog.csdn.net/sinat_35626559/article/details/94553393
中的代码

他写的也挺详细的,但是但是但是有个小坑,还是感谢分享

按照他的步骤我运行的时候发现认证确实可以区分不同的Realm了,授权却是一直走的StaffRealm里的授权,然后Student强转Staff报错。

因为当时只重写了ModularRealmAuthorizer里的isPermitted方法打了断点看看啥情况

人家都不进这个方法里,那区分个毛
ps:主要是我用的注解是@RequiresRoles只检查Role 当时没反应过来

然后我就重写了hasRole方法

终于进方法了,但是但是但是又说我没有角色??? 我都傻了

打断点到hasRole里从return false出去的 卧槽?

检查代码之后发现principals.getRealmNames()只有一个""值,后面匹配相应的Realm的时候都配对不上就return出去了

那principals.getRealmNames()为什么只有""呢?预想应该是"StaffRealm"或者 "StudentRealm"嘛

然后开始了无尽的打断点 无尽的看源码时间

最后知道了这个principal里面有个什么info的东西,正是认证的时候传过去的

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    UsernamePasswordToken userToken = (UsernamePasswordToken)token;
    try{
        String id = userToken.getUsername();
        Student student = studentService.getById(id);
        if(student==null){
            return null;
        }
        return new SimpleAuthenticationInfo(student,student.getPassword(),"");//就是这玩意
    }catch (NumberFormatException e){
        return null;
    }
}

我后面的参数写的就是"",一看源码

public SimpleAuthenticationInfo(Object principal, Object credentials, String realmName) {
    this.principals = new SimplePrincipalCollection(principal, realmName);
    this.credentials = credentials;
}

realmName就是这么传进来的

行吧 我改还不行嘛 然后就有了1、多Realm里写的样子

踩坑完毕!!!好好学习!天天向上!

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值