情景:项目本身shiro集成用的是标准的用户名、密码验证,现需要与第三方平台对接,使用免密码shiro验证,需要多加一个免密码验证的realm,并保证两个realm都可用。
Shiro.xml
1、安全管理器中添加authenticator认证策略配置
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="authenticator" ref="authenticator"/>
<!--<property name="realm" ref="shiroDbRealm"></property>-->
<!--将缓存管理器,交给安全管理器-->
<property name="cacheManager" ref="shiroSpringCacheManager"/>
<!-- 记住密码管理 -->
<property name="rememberMeManager" ref="rememberMeManager"/>
<property name="sessionManager" ref="sessionManager"/>
</bean>
2、在authenticator中配置多realm和具体的认证策略
<bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
<!-- 多realm配置 -->
<property name="realms">
<list>
<ref bean="shiroSSORealm"/> <!-- 免密码realm配置 -->
<ref bean="shiroDbRealm"/> <!-- 正常默认的realm配置 -->
</list>
</property>
<!-- 配置认证策略 -->
<property name="authenticationStrategy">
<!-- 所有Realm皆匹配成功才算成功 -->
<!--<bean class="org.apache.shiro.authc.pam.AllSuccessfulStrategy"></bean>-->
<!-- 只要一个或者多个Realm认证通过就算成功 -->
<bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"></bean>
<!-- 第一个匹配成功即算匹配成功 -->
<!-- <bean class="org.apache.shiro.authc.pam.FirstSuccessfulStrategy"></bean> -->
</property>
</bean>
3、注入新加的realm bean 并自定义凭证匹配规则凭证匹配规则默认实现是CredentialsMatcher类的doCredentialsMatch方法,我们需要继承该类,并重写doCredentialsMatch方法来自定义匹配规则。
<!-- 項目自定义的ssoRealm -->
<bean id="shiroSSORealm" class="com.wf.commons.shiro.ShiroSSORealm">
<constructor-arg name="cacheManager" ref="shiroSpringCacheManager"/>
<constructor-arg index="1" name="matcher" ref="credentialsMatcherSSO"/>
<!-- 启用身份验证缓存,即缓存AuthenticationInfo信息,默认false -->
<property name="authenticationCachingEnabled" value="true"/>
<!-- 缓存AuthenticationInfo信息的缓存名称 -->
<property name="authenticationCacheName" value="authenticationCache"/>
<!-- 缓存AuthorizationInfo信息的缓存名称 -->
<property name="authorizationCacheName" value="authorizationCache"/>
</bean>
<!-- sso免密码登陆 -->
<bean id="credentialsMatcherSSO" class="com.wf.commons.shiro.CredentialsMatcherSSO">
</bean>
public class CredentialsMatcherSSO implements CredentialsMatcher {
@Override
public boolean doCredentialsMatch(AuthenticationToken authenticationToken, AuthenticationInfo authenticationInfo) {
//验证规则写这里,因为ShiroSSORealm中已经对用户名做了判断,这里不做验证直接返回true即可
return true;
}
}
4、实现ShiroDbRealm类
public class ShiroSSORealm extends AuthorizingRealm {
private static final Logger LOGGER = LogManager.getLogger(ShiroSSORealm.class);
@Autowired private IUserService userService;
@Autowired private IRoleService roleService;
public ShiroSSORealm(CacheManager cacheManager, CredentialsMatcher matcher) {
super(cacheManager,matcher);
}
/**
* Shiro登录认证(原理:用户提交 用户名和密码
* --- shiro 封装令牌
* ---- realm 通过用户名将密码查询返回
* ---- shiro 自动去比较查询出密码和用户输入密码是否一致---- 进行登陆控制 )
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authcToken) throws AuthenticationException {
LOGGER.info("Shiro开始登录认证");
//UserNameToken 是存储用户名的实体类,实现了 HostAuthenticationToken, RememberMeAuthenticationToken, Serializable接口,具体写法可参照shiro自带的UsernamePasswordToken来写
UserNameToken token = null;
// UserNameToken,则强转,获取username;否则不处理。
if(authcToken instanceof UserNameToken){
token = (UserNameToken) authcToken;
}else{
return null;
}
UserVo uservo = new UserVo();
uservo.setLoginName(token.getUserName());
List<User> list = userService.selectByLoginName(uservo);
// 账号不存在
if (list == null || list.isEmpty()) {
return null;
}
User user = list.get(0);
// 账号未启用
if (user.getStatus() == 1) {
return null;
}
// 读取用户的url和角色
Map<String, Set<String>> resourceMap = roleService.selectResourceMapByUserId(user.getId());
Set<String> urls = resourceMap.get("urls");
Set<String> roles = resourceMap.get("roles");
ShiroUser shiroUser = new ShiroUser(user.getId(), user.getLoginName(), user.getName(), urls);
shiroUser.setRoles(roles);
// 认证缓存信息,实际上密码的参数并没有卵用,因为我们在验证的具体方法直接返回ture了
return new SimpleAuthenticationInfo(shiroUser, user.getPassword().toCharArray(),
ShiroByteSource.of(user.getSalt()), getName());
}
/**
* Shiro权限认证
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
return null;
}
//这个重写必须加,不然报令牌验证错误,如果只有一个token估计不用
@Override
public boolean supports(AuthenticationToken var1){
return var1 instanceof UserNameToken;
}
}