热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

SpringBoot集成Shiro的多realm实现以及shiro基本入门教程

这篇文章主要介绍了SpringBoot集成Shiro的多realm实现以及shiro基本入门,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

情景

我的项目中有六个用户角色(学校管理员,学生等),需要进行分别登陆。如果在一个realm中,对controller封装好的Token进行Service验证,需要在此realm中注入六个数据库操作对象,然后写一堆if语句来判断应该使用那个Service服务,然后再在验证方法(doGetAuthorizationInfo)中写一堆if来进行分别授权,这样写不仅会让代码可读性会非常低而且很难后期维护修改(刚写完的时候只有上帝和你能看懂你写的是什么,一个月之后你写的是什么就只有上帝能看懂了)。
所以一定要配置多个realm来分别进行认证授权操作。shiro有对多个realm的处理,当配置了多个Realm时,shiro会用自带的org.apache.shiro.authc.pam.ModularRealmAuthenticator类的doAuthenticate方法来进行realm判断,源码:

protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
  assertRealmsConfigured();
  Collection realms = getRealms();
  if (realms.size() == 1) {
   return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);
  } else {
   return doMultiRealmAuthentication(realms, authenticationToken);
  }
 }

assertRealmsConfigured();的作用是验证realm列表是否为空,如果一个realm也没有则会抛出IllegalStateException异常(爆红:Configuration error: No realms have been configured! One or more realms must be present to execute an authentication attempt.)
当realm只有一个时直接返回,当realm有多个时返回所有的realm。而我们要做的就是写多个realm后重写ModularRealmAuthenticator下的doAuthenticate方法,使它能满足我们的项目需求。
那么改怎么重写ModularRealmAuthenticator下的doAuthenticate方法,使它能满足我们的项目需求呢?这就需要分析我们使用shiro的使用方法了。

shiro的使用

1.Controller层中,获取当前用户后将用户名和密码封装UsernamePasswordToken对象,然后调用Subject中的登陆方法subject.login(UsernamePasswordToken)

 @RequestMapping("/user/login")
	@ResponseBody
 public String Login(String userName,String password){
  //获取当前用户 subject
  Subject subject = SecurityUtils.getSubject();
  //封装用户的登陆数据
  UsernamePasswordToken token = 
  new UsernamePasswordToken(userName, password);
  try{
   subject.login(token);//执行登陆方法
   return "登陆成功";
  }catch (UnknownAccountException e){//用户名不存在
   model.addAttribute("msg","用户名不存在");
   return "用户名不存在";
  }catch (IncorrectCredentialsException e){//密码错误
   model.addAttribute("msg","密码错误");
   return "密码错误";
  }
 }

(为了测试方便,我用了@ResponseBody返回字符串)
2.完善自定义Realm类,继承于AuthorizingRealm,主要实现doGetAuthorizationInfo和doGetAuthenticationInfo方法
(需要实现认证和授权方法,在这里方便测试主要是认证)

public class StudentRealm extends AuthorizingRealm {
 @Resource
 private StudentsService studentsService;
 //授权
 @Override
 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
  return null;
 }

 //认证
 @Override
 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
  System.out.println("Shiro=========Student认证");
  UserToken userToken = (UserToken) token;
  Students students = studentsService.queryByNum(userToken.getUsername());
  //账号不存在
  if (students == null) {
   System.out.println("学生不存在");
   //向上层提交UnknownAccountException异常,在controller层处理
   throw new UnknownAccountException();
  }
  //密码认证,shiro来做,可以自定义加密方式
  return new SimpleAuthenticationInfo("", students.getPassword(), USER_LOGIN_TYPE);
 }
}

3.配置shiro,将realm配置进shiro(很多教程是使用xml配置或者ini配置,在这里用java代码配置,功能都是一样的,看个人习惯了)

@Configuration
public class ShiroConfig {
 @Bean
 public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
  ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
  //设置安全管理器
  bean.setSecurityManager(defaultWebSecurityManager);
  return bean;

 }

 //DefaultWebSecurityManager 默认web安全管理器
 @Bean(name = "securityManager")
 public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
  DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
  //关联realm
  securityManager.setRealm(userRealm);
  return securityManager;
 }
 //创建自定义 realm
 @Bean
 public UserRealm userRealm() {
  return new UserRealm();
 }
}

记得加@Configuration注解!!!!!!!

经过以上三步,可以看出shiro的简略工作流程(非常简略)就是,在web 启动阶段,读取
@Configuration注解将自定义的ream配置进默认web安全管理器(DefaultWebSecurityManager)然后将DefaultWebSecurityManager与ShiroFilterFactoryBean相关联。
当用户登陆时,从前端拿到username和password,封装好Token后,进入realm进行认证和授权,而realm就来自于刚才的shiro的DefaultWebSecurityManager配置

多realm实现原理

根据上面的shiro简略流程可知,shiro配置中写入多个realm后,在controller提交token时,只要多携带一个参数,用来进行org.apache.shiro.authc.pam.ModularRealmAuthenticator类的doAuthenticate(重写后)的验证即可明确应该用那个realm。那么,我们需要重写org.apache.shiro.authc.UsernamePasswordToken(令其携带身份参数用于选择realm)和org.apache.shiro.authc.pam.ModularRealmAuthenticator(令其根据token中的身份参数来进行选择realm)即可。

多realm实现具体操作

1.写多个自定义的realm

public class AdminRealm extends AuthorizingRealm {

 @Resource
 private AdminService adminService;

 private static final String USER_LOGIN_TYPE = UserType.AdminRealm;

 @Override
 public String getName() {
  return UserType.AdminRealm;
 }

 @Override
 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
  return null;
 }

 @Override
 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
  System.out.println("Shiro=========Admin认证");
  UserToken userToken = (UserToken) token;
  Admin admin = adminService.queryById(userToken.getUsername());
  if(admin == null){
   System.out.println("管理员不存在");
   throw new UnknownAccountException();
  }
  return new SimpleAuthenticationInfo("", admin.getAdminpassword(), USER_LOGIN_TYPE);
 }
}

2.创建静态变量类(用于realm选择)

public class UserType {
 //实习学校管理员
 public static final String SchoolAdminRealm = "schooladminrealm";

 //学生
 public static final String StudentRealm ="studentrealm";

 //管理员
 public static final String AdminRealm ="adminrealm_1";

 //导员
 public static final String InstructorRealm ="instructorrealm";

 //实习带队老师
 public static final String UniversityteacherRealm ="universityteacherrealm";

 //实习指导老师
 public static final String SchoolTeacherRealm ="schoolteacherrealm";
}

3.重写UsernamePasswordToken,令其可以携带身份参数

@Component
public class UserModularRealmAuthenticator extends ModularRealmAuthenticator {

 @Override
 protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) {
  // 判断getRealms()是否返回为空,ModularRealmAuthenticator 自带
  assertRealmsConfigured();
  // 强制转换回自定义的UserToken
  UserToken token = (UserToken) authenticationToken;
  String loginType = token.getLoginType();
  Collection realms = getRealms();
   for (Realm realm : realms) {
    System.out.println(realm.getName().toLowerCase());
   if (realm.getName().toLowerCase().contains(loginType)){
   //找到登录类型对应的指定Realm
    return doSingleRealmAuthentication(realm, token);
   }
  }
  //没找到正确的realm的异常处理
  String msg = "Configuration error: Didn't find the right realm";
  throw new IllegalStateException(msg);
 }
}

4.shiro的配置中写入自定义的realm,还有其它配置

@Configuration
public class ShiroConfig {

 @Bean
 public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
  ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
  //设置安全管理器
  bean.setSecurityManager(defaultWebSecurityManager);
  return bean;
 }

 //DefaultWebSecurityManager
 @Bean(name = "securityManager")
 public DefaultWebSecurityManager getDefaultWebSecurityManager(
   @Qualifier("schoolAdminRealm") SchoolAdminRealm schoolAdminRealm,
   @Qualifier("studentRealm") StudentRealm studentRealm,
   @Qualifier("adminRealm") AdminRealm adminRealm,
   @Qualifier("schoolTeacherRealm") SchoolTeacherRealm schoolTeacherRealm,
   @Qualifier("instructorRealm") InstructorRealm instructorRealm,
   @Qualifier("universityteacherRealm") UniversityteacherRealm universityteacherRealm,
   @Qualifier("userModularRealmAuthenticator") UserModularRealmAuthenticator userModularRealmAuthenticator
 ) {
  DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
  securityManager.setAuthenticator(userModularRealmAuthenticator);
  /**关联realm
  *securityManager.setRealm() 是配置单个realm,不可用它配置多个realm
  *securityManager.setRealms()配置多个realm, 
  *List realms可以直接被set进去
  */
  List realms = new ArrayList();
  realms.add(schoolAdminRealm);
  realms.add(studentRealm);
  realms.add(adminRealm);
  realms.add(schoolTeacherRealm);
  realms.add(instructorRealm);
  realms.add(universityteacherRealm);
  securityManager.setRealms(realms);
  System.out.println(securityManager.getRealms().toString());
  return securityManager;
 }


 //实习学校管理员
 @Bean(name = "schoolAdminRealm")
 public SchoolAdminRealm SchoolAdminRealm() {
  return new SchoolAdminRealm();
 }

 //学生
 @Bean(name = "studentRealm")
 public StudentRealm StudentRealm() {
  return new StudentRealm();
 }

 //管理员
 @Bean(name = "adminRealm")
 public AdminRealm AdminRealm() {
  return new AdminRealm();
 }

 //导员
 @Bean(name = "instructorRealm")
 public InstructorRealm InstructorRealm() {
  return new InstructorRealm();
 }

 //实习带队老师
 @Bean(name = "universityteacherRealm")
 public UniversityteacherRealm UniversityteacherRealm() {
  return new UniversityteacherRealm();
 }

 //实习指导老师
 @Bean(name = "schoolTeacherRealm")
 public SchoolTeacherRealm SchoolTeacherRealm() {
  return new SchoolTeacherRealm();
 }

}

5.在controller中使用重写后的UsernamePasswordToken(UserToken)即可

//管理员登陆
 @RequestMapping(value = "/AdminLogin", produces = "text/html;charset=UTF-8")
 @ResponseBody//为了测试方便,返回字符串
 public String AdminLogin(
   @RequestParam(value = "username") String username,
   @RequestParam(value = "password") String password) {
  //获取当前用户 subject
  Subject subject = SecurityUtils.getSubject();
  //封装用户的登陆数据
  UserToken token = new UserToken(username, Md5.getMd5(password), USER_LOGIN_TYPE);
  try {
   System.out.println("AdminLogin");
   subject.login(token);//执行登陆方法
   return null;
  } catch (UnknownAccountException e) {//用户名不存在
   System.out.println("用户名错误");
   return null;
  } catch (IncorrectCredentialsException e) {//密码错误
   System.out.println("密码错误");
   return null;
  }

Spring Boot 集成Shiro的多realm配置

到此这篇关于Spring Boot 集成Shiro的多realm实现以及shiro基本入门的文章就介绍到这了,更多相关Spring Boot 集成Shiro内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!


推荐阅读
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文介绍了绕过WAF的XSS检测机制的方法,包括确定payload结构、测试和混淆。同时提出了一种构建XSS payload的方法,该payload与安全机制使用的正则表达式不匹配。通过清理用户输入、转义输出、使用文档对象模型(DOM)接收器和源、实施适当的跨域资源共享(CORS)策略和其他安全策略,可以有效阻止XSS漏洞。但是,WAF或自定义过滤器仍然被广泛使用来增加安全性。本文的方法可以绕过这种安全机制,构建与正则表达式不匹配的XSS payload。 ... [详细]
  • 本文介绍了互联网思维中的三个段子,涵盖了餐饮行业、淘品牌和创业企业的案例。通过这些案例,探讨了互联网思维的九大分类和十九条法则。其中包括雕爷牛腩餐厅的成功经验,三只松鼠淘品牌的包装策略以及一家创业企业的销售额增长情况。这些案例展示了互联网思维在不同领域的应用和成功之道。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了在Mac上搭建php环境后无法使用localhost连接mysql的问题,并通过将localhost替换为127.0.0.1或本机IP解决了该问题。文章解释了localhost和127.0.0.1的区别,指出了使用socket方式连接导致连接失败的原因。此外,还提供了相关链接供读者深入了解。 ... [详细]
  • Webmin远程命令执行漏洞复现及防护方法
    本文介绍了Webmin远程命令执行漏洞CVE-2019-15107的漏洞详情和复现方法,同时提供了防护方法。漏洞存在于Webmin的找回密码页面中,攻击者无需权限即可注入命令并执行任意系统命令。文章还提供了相关参考链接和搭建靶场的步骤。此外,还指出了参考链接中的数据包不准确的问题,并解释了漏洞触发的条件。最后,给出了防护方法以避免受到该漏洞的攻击。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • Android系统移植与调试之如何修改Android设备状态条上音量加减键在横竖屏切换的时候的显示于隐藏
    本文介绍了如何修改Android设备状态条上音量加减键在横竖屏切换时的显示与隐藏。通过修改系统文件system_bar.xml实现了该功能,并分享了解决思路和经验。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • 本文介绍了一些Java开发项目管理工具及其配置教程,包括团队协同工具worktil,版本管理工具GitLab,自动化构建工具Jenkins,项目管理工具Maven和Maven私服Nexus,以及Mybatis的安装和代码自动生成工具。提供了相关链接供读者参考。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • Activiti7流程定义开发笔记
    本文介绍了Activiti7流程定义的开发笔记,包括流程定义的概念、使用activiti-explorer和activiti-eclipse-designer进行建模的方式,以及生成流程图的方法。还介绍了流程定义部署的概念和步骤,包括将bpmn和png文件添加部署到activiti数据库中的方法,以及使用ZIP包进行部署的方式。同时还提到了activiti.cfg.xml文件的作用。 ... [详细]
author-avatar
张。、
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有