Spring Boot 集成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<Realm> 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<Realm> 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<Realm> realms可以直接被set进去
  */
  List<Realm> realms = new ArrayList<Realm>();
  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内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • spring boot整合Shiro实现单点登录的示例代码

    Shiro是什么 Shiro是一个Java平台的开源权限框架,用于认证和访问授权.具体来说,满足对如下元素的支持: 用户,角色,权限(仅仅是操作权限,数据权限必须与业务需求紧密结合),资源(url). 用户分配角色,角色定义权限. 访问授权时支持角色或者权限,并且支持多级的权限定义. Q:对组的支持? A:shiro默认不支持对组设置权限. Q:是否可以满足对组进行角色分配的需求? A:扩展Realm,可以支持对组进行分配角色,其实就是给该组下的所有用户分配权限. Q:对数据权限的支持? 在业务

  • SpringBoot + Shiro前后端分离权限

    shiro 验证通过后的信息保存在session 中,而ajax 每次传的都是不同的sessionid ,所以主要的区别就是需要修改shiro获取sessionid的方式.这里使用的是登录后将后台的sessionid 传到前端然后存放到 cookie(这个存放的地方视情况而定),然后每次请求后端时在Header中携带此信息,这里起名为Authorization shiro 中 默认获取Sessionid的类是 DefaultWebSessionManager 所以需要重写此类 import or

  • Springboot+Shiro记录用户登录信息并获取当前登录用户信息的实现代码

    由于最近做项目需要,在用户登陆后有一个功能是需要用户的信息,进行写入数据库的操作.但是目前还用不到Shiro的高级权限,只为了简单获取用户信息,自己整合了一个只记录用户,获取用户信息的功能. 导入Shiro依赖 <!-- Shiro --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version

  • Spring boot整合shiro+jwt实现前后端分离

    本文实例为大家分享了Spring boot整合shiro+jwt实现前后端分离的具体代码,供大家参考,具体内容如下 这里内容很少很多都为贴的代码,具体内容我经过了看源码和帖子加了注释.帖子就没用太多的内容 先下载shiro和jwt的jar包 <!-- shiro包 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId

  • Spring Boot 集成Shiro的多realm配置过程

    我在做毕设的时候采用shiro进行登录认证和权限管理的实现.其中需求涉及使用三个角色分别是:学生.教师.管理员.现在要三者实现分开登录.即需要三个Realm--StudentRealm和TeacherRealm.AdminRealm,分别处理学生.教师和管理员的验证功能. 但是正常情况下,当定义了多个Realm,无论是学生登录,教师登录,还是管理员登录,都会由这三个Realm共同处理.这是因为,当配置了多个Realm时,我们通常使用的认证器是shiro自带的org.apache.shiro.au

  • SpringBoot集成shiro,MyRealm中无法@Autowired注入Service的问题

    网上说了很多诸如是Spring加载顺序,shiroFilter在Spring自动装配bean之前的问题,其实也有可能忽略如下低级错误. 在ShiroConfiguration中要使用@Bean在ApplicationContext注入MyRealm,不能直接new对象. 道理和Controller中调用Service一样,都要是SpringBean,不能自己new. 错误方式: @Bean(name = "securityManager") public SecurityManager

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

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

  • spring boot 集成shiro的配置方法

    spring boot提供了一个自带的认证框架,同时也提供自定义的javaconfig配置扩展,spring-sercurity同样也是优秀的框架,但是习惯了用apache shiro框架,而且原项目就是集成的shiro框架,到网上找了一下配置方式,没找到完全配置的方法,因此决定自己动手,丰衣足食! 要在spring boot上集成其他框架,首先要会spring javaconfig方法,利用此方法同样可以配置其他模块,废话少说,开始... 开始前需要导入maven依赖(shiro-web可选)

  • spring boot集成shiro详细教程(小结)

    我们开发时候有时候要把传统spring shiro转成spring boot项目,或者直接集成,name我们要搞清楚一个知识,就是 xml配置和spring bean代码配置的关系,这一点很重要,因为spring boot是没有xml配置文件的(也不绝对,spring boot也是可以引用xml配置的) 引入依赖: <dependency> <artifactId>ehcache-core</artifactId> <groupId>net.sf.ehcac

  • 使用Spring Boot集成FastDFS的示例代码

    这篇文章我们介绍如何使用Spring Boot将文件上传到分布式文件系统FastDFS中. 这个项目会在上一个项目的基础上进行构建. 1.pom包配置 我们使用Spring Boot最新版本1.5.9.jdk使用1.8.tomcat8.0. <dependency> <groupId>org.csource</groupId> <artifactId>fastdfs-client-java</artifactId> <version>

  • Spring Boot集成MyBatis访问数据库的方法

    基于spring boot开发的微服务应用,与MyBatis如何集成? 集成方法 可行的方法有: 1.基于XML或者Java Config,构建必需的对象,配置MyBatis. 2.使用MyBatis官方提供的组件,实现MyBatis的集成. 方法一 建议参考如下文章,完成集成的验证. MyBatis学习 之 一.MyBatis简介与配置MyBatis+Spring+MySql 基于Spring + Spring MVC + Mybatis 高性能web构建 spring与mybatis三种整合

  • Spring Boot 集成Mybatis实现主从(多数据源)分离方案示例

    本文将介绍使用Spring Boot集成Mybatis并实现主从库分离的实现(同样适用于多数据源).延续之前的Spring Boot 集成MyBatis.项目还将集成分页插件PageHelper.通用Mapper以及Druid. 新建一个Maven项目,最终项目结构如下: 多数据源注入到sqlSessionFactory POM增加如下依赖: <!--JSON--> <dependency> <groupId>com.fasterxml.jackson.core<

  • Spring Boot集成Redis实现缓存机制(从零开始学Spring Boot)

    本文章牵涉到的技术点比较多:spring Data JPA.Redis.Spring MVC,Spirng Cache,所以在看这篇文章的时候,需要对以上这些技术点有一定的了解或者也可以先看看这篇文章,针对文章中实际的技术点在进一步了解(注意,您需要自己下载Redis Server到您的本地,所以确保您本地的Redis可用,这里还使用了MySQL数据库,当然你也可以内存数据库进行测试).这篇文章会提供对应的Eclipse代码示例,具体大体的分如下几个步骤: (1)新建Java Maven Pro

  • 详解Spring Boot集成MyBatis(注解方式)

    MyBatis是支持定制化SQL.存储过程以及高级映射的优秀的持久层框架,避免了几乎所有的JDBC代码和手动设置参数以及获取结果集.spring Boot是能支持快速创建Spring应用的Java框架.本文通过一个例子来学习Spring Boot如何集成MyBatis,而且过程中不需要XML配置. 创建数据库 本文的例子使用MySQL数据库,首先创建一个用户表,执行sql语句如下: CREATE TABLE IF NOT EXISTS user ( `id` INT(10) NOT NULL A

  • 详解spring Boot 集成 Thymeleaf模板引擎实例

    今天学习了spring boot 集成Thymeleaf模板引擎.发现Thymeleaf功能确实很强大.记录于此,供自己以后使用. Thymeleaf: Thymeleaf是一个java类库,他是一个xml/xhtml/html5的模板引擎,可以作为mvc的web应用的view层. Thymeleaf还提供了额外的模块与Spring MVC集成,所以我们可以使用Thymeleaf完全替代jsp. spring Boot 通过org.springframework.boot.autoconfigu

随机推荐