springboot shiro 多realm配置认证、授权
shiro进行登录认证和权限管理的实现。其中需求涉及使用两个角色分别是:门店,公司。现在要两者实现分开登录。即需要两个Realm——MyShiroRealmSHOP和MyShiroRealmCOMPANY,分别处理门店,公司的验证功能。
但是正常情况下,当定义了多个Realm,无论是门店登录还是公司登录,都会由这两个Realm共同处理。这是因为,当配置了多个Realm时,我们通常使用的认证器是shiro自带的org.apache.shiro.authc.pam.ModularRealmAuthenticator,其中决定使用的Realm的是doAuthenticate()方法,源代码如下:
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);
}
}
上述代码的意思就是如果有多个Realm就会使用所有配置的Realm。 只有一个的时候,就直接使用当前的Realm。
为了实现需求,我会创建一个org.apache.shiro.authc.pam.ModularRealmAuthenticator的子类,并重写doAuthenticate()方法,让特定的Realm完成特定的功能。如何区分呢?我会同时创建一个org.apache.shiro.authc.UsernamePasswordToken的子类,在其中添加一个字段VirtualType,用来标识登录的类型,即是门店登录还是公司登录。具体步骤如下:
public enum VirtualType {
COMPANY, // 公司
SHOP // 门店
}
接下来新建org.apache.shiro.authc.UsernamePasswordToken的子类UserToken
import org.apache.shiro.authc.UsernamePasswordToken;
public class UserToken extends UsernamePasswordToken {
private VirtualType virtualType;
public UserToken(final String username, final String password, VirtualType virtualType) {
super(username, password);
this.virtualType = virtualType;
}
public VirtualType getVirtualType() {
return virtualType;
}
public void setVirtualType(VirtualType virtualType) {
this.virtualType = virtualType;
}
}
新建org.apache.shiro.authc.pam.ModularRealmAuthenticator的子类UserModularRealmAuthenticator:
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.realm.Realm; public class UserModularRealmAuthenticator extends ModularRealmAuthenticator { private static final Logger logger = LoggerFactory.getLogger(UserModularRealmAuthenticator.class); @Override
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)
throws AuthenticationException {
logger.info("UserModularRealmAuthenticator:method doAuthenticate() execute ");
// 判断getRealms()是否返回为空
assertRealmsConfigured();
// 强制转换回自定义的CustomizedToken
UserToken userToken = (UserToken) authenticationToken;
// 登录类型
VirtualType virtualType = userToken.getVirtualType();
// 所有Realm
Collection<Realm> realms = getRealms();
// 登录类型对应的所有Realm
Collection<Realm> typeRealms = new ArrayList<>();
for (Realm realm : realms) {
if (realm.getName().contains(virtualType.toString())) // 注:这里使用类名包含枚举,区分realm
typeRealms.add(realm);
}
// 判断是单Realm还是多Realm
if (typeRealms.size() == 1) {
logger.info("doSingleRealmAuthentication() execute ");
return doSingleRealmAuthentication(typeRealms.iterator().next(), userToken);
} else {
logger.info("doMultiRealmAuthentication() execute ");
return doMultiRealmAuthentication(typeRealms, userToken);
}
}
}
创建分别处理门店登录还是公司登录的Realm:
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired; import java.util.HashSet;
import java.util.Set; /**公司登陆realm
*/
public class MyShiroRealmCOMPANY extends AuthorizingRealm { @Autowired
IUserService userService; @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal(); // 一定是String类型,在SimpleAuthenticationInfo
SystemUser systemUser = userService.getUserByName(username, VirtualType.COMPANY);
if (systemUser == null) {
throw new RuntimeException("system concurrent exception: COMPANY user not found:username=" + username);
} SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); Set<String> stringPermissions = new HashSet<>(256);
// 字符串资源
authorizationInfo.addStringPermissions(stringPermissions);
return authorizationInfo;
} @Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
UserToken token = (UserToken)authenticationToken;
// 逻辑登陆
return new SimpleAuthenticationInfo(token.getPrincipal(), token.getCredentials(), getName());
}
}
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired; import java.util.HashSet;
import java.util.Set; /**门店登陆realm
*/
public class MyShiroRealmSHOP extends AuthorizingRealm { @Autowired
IUserService userService; @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal(); // 一定是String类型,在SimpleAuthenticationInfo
SystemUser systemUser = userService.getUserByName(username, VirtualType.SHOP);
if (systemUser == null) {
throw new RuntimeException("system concurrent exception: SHOP user not found:username=" + username);
} SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); Set<String> stringPermissions = new HashSet<>(256);
// 字符串资源
authorizationInfo.addStringPermissions(stringPermissions);
return authorizationInfo;
} @Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
UserToken token = (UserToken)authenticationToken;
// 逻辑登陆
return new SimpleAuthenticationInfo(token.getPrincipal(), token.getCredentials(), getName());
}
}
ShiroConfig配置
@Bean("securityManager")
public SecurityManager securityManager(RedisTemplate redisTemplate) { // redisTemplate配置的redis缓存,可忽略
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
List<Realm> realms = new ArrayList<>();
//添加多个Realm
realms.add(myShiroRealmSHOP(redisTemplate));
realms.add(myShiroRealmCOMPANY(redisTemplate));
securityManager.setAuthenticator(modularRealmAuthenticator()); // 需要再realm定义之前
securityManager.setRealms(realms);
securityManager.setSessionManager(myShiroSession(redisTemplate));
return securityManager;
}
/**
* 系统自带的Realm管理,主要针对多realm 认证
*/
@Bean
public ModularRealmAuthenticator modularRealmAuthenticator() {
//自己重写的ModularRealmAuthenticator
UserModularRealmAuthenticator modularRealmAuthenticator = new UserModularRealmAuthenticator();
modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
return modularRealmAuthenticator;
}
@Bean("myShiroRealmSHOP")
public MyShiroRealmSHOP myShiroRealmSHOP(RedisTemplate redisTemplate) {
return new MyShiroRealmSHOP();
}
@Bean("myShiroRealmCOMPANY")
public MyShiroRealmCOMPANY myShiroRealmCOMPANY(RedisTemplate redisTemplate) {
return new MyShiroRealmCOMPANY();
}
登陆即可:
subject.login(new UserToken(username, password, virtualType))
这里需要注意的是,上述配置的Authenticator主要针对登陆认证,对于授权时没有控制的,使用资源注入时会发现,使用的是myShiroRealmSHOP的doGetAuthorizationInfo方法(上面SHOP的定义在前),没有走对应的realm的授权,产生问题错乱;
新建org.apache.shiro.authz.ModularRealmAuthorizer子类:
import org.apache.shiro.authz.Authorizer;
import org.apache.shiro.authz.ModularRealmAuthorizer;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.subject.PrincipalCollection;
public class UserModularRealmAuthorizer extends ModularRealmAuthorizer {
@Override
public boolean isPermitted(PrincipalCollection principals, String permission) {
assertRealmsConfigured();
for (Realm realm : getRealms()) {
if (!(realm instanceof Authorizer)){ continue;}
// todo 授权配置
if (realm.getName().contains(VirtualType.COMPANY.toString())) { // 判断realm
if (permission.contains("company")) { // 判断是否改realm的资源
return ((MyShiroRealmCOMPANY) realm).isPermitted(principals, permission); // 使用改realm的授权方法
}
}
if (realm.getName().contains(VirtualType.SHOP.toString())) {
if (permission.contains("shop")) {
return ((MyShiroRealmSHOP) realm).isPermitted(principals, permission);
}
}
}
return false;
}
}
然后在ShiroConfig更改:
@Bean("securityManager")
public SecurityManager securityManager(RedisTemplate redisTemplate) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 使用注解,@RequiresPermissions,读取缓存权限信息保存key对象为:{@link org.apache.shiro.subject.SimplePrincipalCollection},所以redis缓存配置的String不能转换
// securityManager.setCacheManager(redisCacheManager(redisTemplate));
List<Realm> realms = new ArrayList<>();
//添加多个Realm
realms.add(myShiroRealmSHOP(redisTemplate));
realms.add(myShiroRealmCOMPANY(redisTemplate));
securityManager.setAuthenticator(modularRealmAuthenticator());
securityManager.setAuthorizer(modularRealmAuthorizer()); // 这里
securityManager.setRealms(realms);
securityManager.setSessionManager(myShiroSession(redisTemplate));
return securityManager;
}
/**
* 系统自带的Realm管理,主要针对多realm 认证
*/
@Bean
public ModularRealmAuthenticator modularRealmAuthenticator() {
//自己重写的ModularRealmAuthenticator
UserModularRealmAuthenticator modularRealmAuthenticator = new UserModularRealmAuthenticator();
modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
return modularRealmAuthenticator;
}
/**
* 系统自带的Realm管理,主要针对多realm 授权
*/
@Bean
public ModularRealmAuthorizer modularRealmAuthorizer() {
//自己重写的ModularRealmAuthorizer
UserModularRealmAuthorizer modularRealmAuthorizer = new UserModularRealmAuthorizer();
return modularRealmAuthorizer;
}
@Bean("myShiroRealmSHOP")
public MyShiroRealmSHOP myShiroRealmSHOP(RedisTemplate redisTemplate) {
return new MyShiroRealmSHOP();
}
@Bean("myShiroRealmCOMPANY")
public MyShiroRealmCOMPANY myShiroRealmCOMPANY(RedisTemplate redisTemplate) {
return new MyShiroRealmCOMPANY();
}
参考:https://blog.csdn.net/cckevincyh/article/details/79629022
springboot shiro 多realm配置认证、授权的更多相关文章
- SpringBoot整合shiro实现用户的认证授权
* 项目环境搭建 * 配置ShiroConfig,用于shiro的基本配置和注入自定义规则 * 实现自定义的realm,继承AuthorizingRealm * 编写测试controller和页面 基 ...
- Shiro学习笔记 三(认证授权)
第一种首先基于角色的权限控制 1.由于不断的创建SecurityFactory工程等步骤重复多次,所以应该将这些步骤封装成一个工具类 还是首先看一下目录结构 主要用到文件 首先贴一下工具类的方法 pa ...
- 30分钟了解Shiro与Springboot的多Realm基础配置
写在前面的话: 我之前写过两篇与shiro安全框架有关的博文,居然能够广受欢迎实在令人意外.说明大家在互联网时代大伙对于安全和登录都非常重视,无论是大型项目还是中小型业务,普遍都至少需要登录与认证的逻 ...
- SpringBoot 集成Shiro之使用Redis缓存授权认证信息
因为用户认证与授权需要从数据库中查询并验证信息,但是对于权限很少改变的情况,这样不断从数据库中查询角色验证权限,对整个系统的开销很大,对数据库压力也随之增大.因此可以将用户认证和授权信息都缓存起来,第 ...
- 【原】无脑操作:IDEA + maven + Shiro + SpringBoot + JPA + Thymeleaf实现基础认证权限
开发环境搭建参见<[原]无脑操作:IDEA + maven + SpringBoot + JPA + Thymeleaf实现CRUD及分页> 需求: ① 除了登录页面,在地址栏直接访问其他 ...
- springboot shiro和freemarker集成之权限控制完全参考手册(跳过认证,登录由三方验证,全网首发)
本文主要考虑单点登录场景,登录由其他系统负责,业务子系统只使用shiro进行菜单和功能权限校验,登录信息通过token从redis取得,这样登录验证和授权就相互解耦了. 用户.角色.权限进行集中式管理 ...
- Spring Cloud之路:(七)SpringBoot+Shiro实现登录认证和权限管理
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/sage_wang/article/details/79592269一.Shiro介绍1.Shiro是 ...
- (转)Springboot+shiro配置笔记+错误小结
springboot不像springmvc,它没有xml配置文件,那该如何配置shiro呢,其实也不难,用java代码+注解来解决这个问题.仅以此篇记录我对shiro的学习,如有对过客造成不便,实在抱 ...
- Springboot+shiro配置笔记+错误小结
软件152 尹以操 springboot不像springmvc,它没有xml配置文件,那该如何配置shiro呢,其实也不难,用java代码+注解来解决这个问题.仅以此篇记录我对shiro的学习,如有对 ...
随机推荐
- WEEX SDK集成到工程(Integrate to Android) #25
WEEX SDK集成到工程(Integrate to Android) #25 https://github.com/weexteam/article/issues/25
- Windows下Word.exe在哪?
在这里: C:\Program Files\Microsoft Office\root\Office16
- SQL 数据库性能优化
http://blog.csdn.net/yzllz001/article/details/54848513 1. 减少数据访问(减少磁盘访问) 2. 返回更少数据(减少网络传输或磁盘访问) 3. ...
- node-express项目的搭建并通过mongoose操作MongoDB实现增删改查分页排序(四)
最近写了一个用node来操作MongoDB完成增.删.改.查.排序.分页功能的示例,并且已经放在了服务器上地址:http://39.105.32.180:3333. Mongoose是在node.js ...
- Sybase to Oracle Golden Gate
Sybase 安装Golden Gate: 下载,然后create subdirs.并且在两端配置好mgr,设置好端口7809 创建golden gate用户ogguser,并且给它授权sa和repl ...
- 微信小程序调试 Webview
document.querySelectorAll("webview")[1].showDevTools(true);
- web开发必看:你的网站支持https吗?
如果有一项技术可以让网站的访问速度更快.更安全.并且seo权重提升(百度除外),而且程序员不需要改代码就可以全站使用,最重要的是,不需要额外花钱,那有这么好的事情吗? HTTP通信协议是全球万维网ww ...
- 获取浏览器端的cookie方法
代码如下: function getCookie(key){ var cookies=document.cookie; if(cookies.length>0){ var start=cooki ...
- 亲历:IT 从业者避免猝死攻略 v1.0
作者:香蕉痞 出处:http://www.geekpark.net/read/view/191188?u=0 亲历:IT 从业者避免猝死攻略 v1.0 By 香蕉痞 | 2013/10/28 [核心提 ...
- GitHub两种上传方式的对比----SSH / https
https://www.jianshu.com/p/1ac06bcd8ab5 https://www.cnblogs.com/lqfxyy/p/5740720.html https://blog.cs ...