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判断,源码:
protectedAuthenticationInfodoAuthenticate(AuthenticationTokenauthenticationToken)throwsAuthenticationException{ assertRealmsConfigured(); Collectionrealms=getRealms(); if(realms.size()==1){ returndoSingleRealmAuthentication(realms.iterator().next(),authenticationToken); }else{ returndoMultiRealmAuthentication(realms,authenticationToken); } }
assertRealmsConfigured();的作用是验证realm列表是否为空,如果一个realm也没有则会抛出IllegalStateException异常(爆红:Configurationerror:Norealmshavebeenconfigured!Oneormorerealmsmustbepresenttoexecuteanauthenticationattempt.)
当realm只有一个时直接返回,当realm有多个时返回所有的realm。而我们要做的就是写多个realm后重写ModularRealmAuthenticator下的doAuthenticate方法,使它能满足我们的项目需求。
那么改怎么重写ModularRealmAuthenticator下的doAuthenticate方法,使它能满足我们的项目需求呢?这就需要分析我们使用shiro的使用方法了。
shiro的使用
1.Controller层中,获取当前用户后将用户名和密码封装UsernamePasswordToken对象,然后调用Subject中的登陆方法subject.login(UsernamePasswordToken)
@RequestMapping("/user/login") @ResponseBody publicStringLogin(StringuserName,Stringpassword){ //获取当前用户subject Subjectsubject=SecurityUtils.getSubject(); //封装用户的登陆数据 UsernamePasswordTokentoken= newUsernamePasswordToken(userName,password); try{ subject.login(token);//执行登陆方法 return"登陆成功"; }catch(UnknownAccountExceptione){//用户名不存在 model.addAttribute("msg","用户名不存在"); return"用户名不存在"; }catch(IncorrectCredentialsExceptione){//密码错误 model.addAttribute("msg","密码错误"); return"密码错误"; } }
(为了测试方便,我用了@ResponseBody返回字符串)
2.完善自定义Realm类,继承于AuthorizingRealm,主要实现doGetAuthorizationInfo和doGetAuthenticationInfo方法
(需要实现认证和授权方法,在这里方便测试主要是认证)
publicclassStudentRealmextendsAuthorizingRealm{ @Resource privateStudentsServicestudentsService; //授权 @Override protectedAuthorizationInfodoGetAuthorizationInfo(PrincipalCollectionprincipals){ returnnull; } //认证 @Override protectedAuthenticationInfodoGetAuthenticationInfo(AuthenticationTokentoken)throwsAuthenticationException{ System.out.println("Shiro=========Student认证"); UserTokenuserToken=(UserToken)token; Studentsstudents=studentsService.queryByNum(userToken.getUsername()); //账号不存在 if(students==null){ System.out.println("学生不存在"); //向上层提交UnknownAccountException异常,在controller层处理 thrownewUnknownAccountException(); } //密码认证,shiro来做,可以自定义加密方式 returnnewSimpleAuthenticationInfo("",students.getPassword(),USER_LOGIN_TYPE); } }
3.配置shiro,将realm配置进shiro(很多教程是使用xml配置或者ini配置,在这里用java代码配置,功能都是一样的,看个人习惯了)
@Configuration publicclassShiroConfig{ @Bean publicShiroFilterFactoryBeangetShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManagerdefaultWebSecurityManager){ ShiroFilterFactoryBeanbean=newShiroFilterFactoryBean(); //设置安全管理器 bean.setSecurityManager(defaultWebSecurityManager); returnbean; } //DefaultWebSecurityManager默认web安全管理器 @Bean(name="securityManager") publicDefaultWebSecurityManagergetDefaultWebSecurityManager(@Qualifier("userRealm")UserRealmuserRealm){ DefaultWebSecurityManagersecurityManager=newDefaultWebSecurityManager(); //关联realm securityManager.setRealm(userRealm); returnsecurityManager; } //创建自定义realm @Bean publicUserRealmuserRealm(){ returnnewUserRealm(); } }
记得加@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
publicclassAdminRealmextendsAuthorizingRealm{ @Resource privateAdminServiceadminService; privatestaticfinalStringUSER_LOGIN_TYPE=UserType.AdminRealm; @Override publicStringgetName(){ returnUserType.AdminRealm; } @Override protectedAuthorizationInfodoGetAuthorizationInfo(PrincipalCollectionprincipals){ returnnull; } @Override protectedAuthenticationInfodoGetAuthenticationInfo(AuthenticationTokentoken)throwsAuthenticationException{ System.out.println("Shiro=========Admin认证"); UserTokenuserToken=(UserToken)token; Adminadmin=adminService.queryById(userToken.getUsername()); if(admin==null){ System.out.println("管理员不存在"); thrownewUnknownAccountException(); } returnnewSimpleAuthenticationInfo("",admin.getAdminpassword(),USER_LOGIN_TYPE); } }
2.创建静态变量类(用于realm选择)
publicclassUserType{ //实习学校管理员 publicstaticfinalStringSchoolAdminRealm="schooladminrealm"; //学生 publicstaticfinalStringStudentRealm="studentrealm"; //管理员 publicstaticfinalStringAdminRealm="adminrealm_1"; //导员 publicstaticfinalStringInstructorRealm="instructorrealm"; //实习带队老师 publicstaticfinalStringUniversityteacherRealm="universityteacherrealm"; //实习指导老师 publicstaticfinalStringSchoolTeacherRealm="schoolteacherrealm"; }
3.重写UsernamePasswordToken,令其可以携带身份参数
@Component publicclassUserModularRealmAuthenticatorextendsModularRealmAuthenticator{ @Override protectedAuthenticationInfodoAuthenticate(AuthenticationTokenauthenticationToken){ //判断getRealms()是否返回为空,ModularRealmAuthenticator自带 assertRealmsConfigured(); //强制转换回自定义的UserToken UserTokentoken=(UserToken)authenticationToken; StringloginType=token.getLoginType(); Collectionrealms=getRealms(); for(Realmrealm:realms){ System.out.println(realm.getName().toLowerCase()); if(realm.getName().toLowerCase().contains(loginType)){ //找到登录类型对应的指定Realm returndoSingleRealmAuthentication(realm,token); } } //没找到正确的realm的异常处理 Stringmsg="Configurationerror:Didn'tfindtherightrealm"; thrownewIllegalStateException(msg); } }
4.shiro的配置中写入自定义的realm,还有其它配置
@Configuration publicclassShiroConfig{ @Bean publicShiroFilterFactoryBeangetShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManagerdefaultWebSecurityManager){ ShiroFilterFactoryBeanbean=newShiroFilterFactoryBean(); //设置安全管理器 bean.setSecurityManager(defaultWebSecurityManager); returnbean; } //DefaultWebSecurityManager @Bean(name="securityManager") publicDefaultWebSecurityManagergetDefaultWebSecurityManager( @Qualifier("schoolAdminRealm")SchoolAdminRealmschoolAdminRealm, @Qualifier("studentRealm")StudentRealmstudentRealm, @Qualifier("adminRealm")AdminRealmadminRealm, @Qualifier("schoolTeacherRealm")SchoolTeacherRealmschoolTeacherRealm, @Qualifier("instructorRealm")InstructorRealminstructorRealm, @Qualifier("universityteacherRealm")UniversityteacherRealmuniversityteacherRealm, @Qualifier("userModularRealmAuthenticator")UserModularRealmAuthenticatoruserModularRealmAuthenticator ){ DefaultWebSecurityManagersecurityManager=newDefaultWebSecurityManager(); securityManager.setAuthenticator(userModularRealmAuthenticator); /**关联realm *securityManager.setRealm()是配置单个realm,不可用它配置多个realm *securityManager.setRealms()配置多个realm, *Listrealms可以直接被set进去 */ List realms=newArrayList (); 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()); returnsecurityManager; } //实习学校管理员 @Bean(name="schoolAdminRealm") publicSchoolAdminRealmSchoolAdminRealm(){ returnnewSchoolAdminRealm(); } //学生 @Bean(name="studentRealm") publicStudentRealmStudentRealm(){ returnnewStudentRealm(); } //管理员 @Bean(name="adminRealm") publicAdminRealmAdminRealm(){ returnnewAdminRealm(); } //导员 @Bean(name="instructorRealm") publicInstructorRealmInstructorRealm(){ returnnewInstructorRealm(); } //实习带队老师 @Bean(name="universityteacherRealm") publicUniversityteacherRealmUniversityteacherRealm(){ returnnewUniversityteacherRealm(); } //实习指导老师 @Bean(name="schoolTeacherRealm") publicSchoolTeacherRealmSchoolTeacherRealm(){ returnnewSchoolTeacherRealm(); } }
5.在controller中使用重写后的UsernamePasswordToken(UserToken)即可
//管理员登陆 @RequestMapping(value="/AdminLogin",produces="text/html;charset=UTF-8") @ResponseBody//为了测试方便,返回字符串 publicStringAdminLogin( @RequestParam(value="username")Stringusername, @RequestParam(value="password")Stringpassword){ //获取当前用户subject Subjectsubject=SecurityUtils.getSubject(); //封装用户的登陆数据 UserTokentoken=newUserToken(username,Md5.getMd5(password),USER_LOGIN_TYPE); try{ System.out.println("AdminLogin"); subject.login(token);//执行登陆方法 returnnull; }catch(UnknownAccountExceptione){//用户名不存在 System.out.println("用户名错误"); returnnull; }catch(IncorrectCredentialsExceptione){//密码错误 System.out.println("密码错误"); returnnull; }
SpringBoot集成Shiro的多realm配置
到此这篇关于SpringBoot集成Shiro的多realm实现以及shiro基本入门的文章就介绍到这了,更多相关SpringBoot集成Shiro内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!