Skip to content

Commit

Permalink
Shiro 添加多 realm 实现
Browse files Browse the repository at this point in the history
  • Loading branch information
nineya committed Feb 17, 2021
1 parent 1c7fbe4 commit 0add169
Show file tree
Hide file tree
Showing 12 changed files with 395 additions and 11 deletions.
83 changes: 83 additions & 0 deletions shiro-study/src/main/java/com/nineya/shiro/config/ManageRealm.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.nineya.shiro.config;

import com.nineya.shiro.entity.*;
import com.nineya.shiro.service.LoginService;
import com.nineya.shiro.util.UserTokenUtil;
import org.apache.catalina.realm.AuthenticatedUserRealm;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.stream.Collectors;

/**
* 自定义管理员的 realm
*
* @author 殇雪话诀别
* 2021/2/15
*/
public class ManageRealm extends AuthorizingRealm {
@Resource
private LoginService loginService;
@Resource
private UserTokenUtil tokenUtil;

@Override
public boolean supports(AuthenticationToken token) {
return token instanceof JwtToken;
}

/**
* 授权,在认证之后执行
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println(this.getClass().getName());
String token = (String) principals.getPrimaryPrincipal();
String name = tokenUtil.getUserName(token);
Manage manage = loginService.getManageById(Long.parseLong(name));
if (manage == null) {
return null;
}
// 添加角色和权限
SimpleAuthorizationInfo simpleAuthenticationInfo = new SimpleAuthorizationInfo();
for (Role role : manage.getRoles()) {
// 添加角色
simpleAuthenticationInfo.addRole(role.getRoleName());
// 添加权限
simpleAuthenticationInfo.addStringPermissions(role.getPermissions().stream()
.map(Permissions::getPermissionsName).collect(Collectors.toSet()));
}
return simpleAuthenticationInfo;
}

/**
* 认证
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println(this.getClass().getName());
if (StringUtils.isEmpty(token.getPrincipal())) {
return null;
}
String name = tokenUtil.getUserName((String) token.getPrincipal());
Manage manage = loginService.getManageById(Long.parseLong(name));
if (manage == null) {
return null;
}
// 第一个参数是主体,将会在授权时封装成PrincipalCollection.getPrimaryPrincipal()进行使用,所以必须将jwt内容传回
// 第二个参数是认证信息,即密码,为后面验证可以通过,需要和token中的内容一样
// 第三个参数是领域名称
return new SimpleAuthenticationInfo(token.getPrincipal(), token.getCredentials(), getName());
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.nineya.shiro.config;

import com.nineya.shiro.entity.LoginType;
import com.nineya.shiro.filter.TokenFilter;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.authz.ModularRealmAuthorizer;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
Expand All @@ -11,7 +15,9 @@
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
Expand All @@ -24,6 +30,7 @@ public class ShiroConfiguration {

/**
* 配置代理,没有配置将会导致注解不生效
*
* @return
*/
@Bean
Expand All @@ -35,6 +42,7 @@ public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {

/**
* 配置代理,没有配置将会导致注解不生效
*
* @return
*/
@Bean
Expand All @@ -46,17 +54,33 @@ public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(S

/**
* 将自己的验证方式加入容器
*
* @return
*/
@Bean
public Realm studyRealm() {
StudyRealm studyRealm = new StudyRealm();
studyRealm.setName(LoginType.USER.name());
return studyRealm;
}

/**
* 将自己的验证方式加入容器
*
* @return
*/
@Bean
public Realm manageRealm() {
ManageRealm manageRealm = new ManageRealm();
manageRealm.setName(LoginType.MANAGE.name());
return manageRealm;
}


/**
* 不应该将过滤器的实现注册为bean,否则会导致Filter过滤器顺序混乱,导致抛出异常
* 如果一定要注册为 Bean,可以使用 Order 指定优先级,还未尝试过
*
* @return
*/
public TokenFilter tokenFilter() {
Expand All @@ -65,12 +89,13 @@ public TokenFilter tokenFilter() {

/**
* Filter工厂,设置对应的过滤条件和跳转条件
*
* @return
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean() {
public ShiroFilterFactoryBean shiroFilterFactoryBean(List<Realm> realms) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager());
shiroFilterFactoryBean.setSecurityManager(securityManager(realms));
Map<String, String> map = new HashMap<>();
//登出
map.put("/logout", "logout");
Expand All @@ -83,19 +108,44 @@ public ShiroFilterFactoryBean shiroFilterFactoryBean() {
//错误页面,认证不通过跳转
shiroFilterFactoryBean.setUnauthorizedUrl("/error");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
shiroFilterFactoryBean.setFilters(new HashMap<String, Filter>(){{put("jwt", tokenFilter());}});
shiroFilterFactoryBean.setFilters(new HashMap<String, Filter>() {{
put("jwt", tokenFilter());
}});
return shiroFilterFactoryBean;
}

/**
* 权限管理,配置主要是Realm的管理认证,同时可以配置缓存管理等
*
* @return
*/
@Bean
public DefaultWebSecurityManager securityManager() {
public DefaultWebSecurityManager securityManager(List<Realm> realms) {
DefaultWebSecurityManager webSecurityManager = new DefaultWebSecurityManager();
//realm管理
webSecurityManager.setRealm(studyRealm());
webSecurityManager.setAuthenticator(modularRealmAuthenticator());
webSecurityManager.setAuthorizer(modularRealmAuthorizer());
//realm管理,必须在两个modular之后,因为会对这两个对象进行设值
webSecurityManager.setRealms(realms);
return webSecurityManager;
}

/**
* 针对多realm,用于认证阶段
*/
@Bean
public ModularRealmAuthenticator modularRealmAuthenticator() {
//自己重写的ModularRealmAuthenticator
StudyModularRealmAuthenticator modularRealmAuthenticator = new StudyModularRealmAuthenticator();
modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
return modularRealmAuthenticator;
}

/**
* 针对多realm,用于授权阶段
* @return
*/
@Bean
public ModularRealmAuthorizer modularRealmAuthorizer() {
return new StudyModularRealmAuthorizer();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.nineya.shiro.config;

import com.nineya.shiro.entity.JwtToken;
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;

import java.util.Collection;

/**
* @author 殇雪话诀别
* 2021/2/17
*/
public class StudyModularRealmAuthenticator extends ModularRealmAuthenticator {
@Override
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
// 判断 Realm 是否为空
assertRealmsConfigured();
Collection<Realm> realms = getRealms();
JwtToken jwtToken = (JwtToken) authenticationToken;
String loginType = jwtToken.getLoginType().name();
for (Realm realm : realms) {
if (realm.getName().equals(loginType)) {
return doSingleRealmAuthentication(realm, authenticationToken);
}
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.nineya.shiro.config;

import org.apache.shiro.authz.Authorizer;
import org.apache.shiro.authz.ModularRealmAuthorizer;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.subject.PrincipalCollection;

import java.util.Set;

/**
* @author 殇雪话诀别
* 2021/2/18
*/
public class StudyModularRealmAuthorizer extends ModularRealmAuthorizer {
@Override
public boolean isPermitted(PrincipalCollection principals, String permission) {
assertRealmsConfigured();
Set<String> realmNames = principals.getRealmNames();
for (Realm realm : getRealms()) {
if (!(realm instanceof Authorizer)) continue;
// 仅比较 realmName 对应得上的 realm
if (realmNames.contains(realm.getName())) {
return ((Authorizer) realm).isPermitted(principals, permission);
}
}
return false;
}

@Override
public boolean isPermitted(PrincipalCollection principals, Permission permission) {
assertRealmsConfigured();
Set<String> realmNames = principals.getRealmNames();
for (Realm realm : getRealms()) {
if (!(realm instanceof Authorizer)) continue;
// 仅比较 realmName 对应得上的 realm
if (realmNames.contains(realm.getName())) {
return ((Authorizer) realm).isPermitted(principals, permission);
}
}
return false;
}

@Override
public boolean hasRole(PrincipalCollection principals, String roleIdentifier) {
assertRealmsConfigured();
Set<String> realmNames = principals.getRealmNames();
for (Realm realm : getRealms()) {
if (!(realm instanceof Authorizer)) continue;
// 仅比较 realmName 对应得上的 realm
if (realmNames.contains(realm.getName())) {
return ((Authorizer) realm).hasRole(principals, roleIdentifier);
}
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.nineya.shiro.config;

import com.nineya.shiro.entity.JwtToken;
import com.nineya.shiro.entity.Permissions;
import com.nineya.shiro.entity.Role;
import com.nineya.shiro.entity.User;
Expand Down Expand Up @@ -31,7 +32,7 @@ public class StudyRealm extends AuthorizingRealm {

@Override
public boolean supports(AuthenticationToken token) {
return token instanceof BearerToken;
return token instanceof JwtToken;
}

/**
Expand All @@ -41,6 +42,7 @@ public boolean supports(AuthenticationToken token) {
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println(this.getClass().getName());
String token = (String) principals.getPrimaryPrincipal();
String name = tokenUtil.getUserName(token);
User user = loginService.getUserByName(name);
Expand All @@ -64,6 +66,7 @@ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println(this.getClass().getName());
if (StringUtils.isEmpty(token.getPrincipal())) {
return null;
}
Expand All @@ -75,6 +78,6 @@ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
// 第一个参数是主体,将会在授权时封装成PrincipalCollection.getPrimaryPrincipal()进行使用,所以必须将jwt内容传回
// 第二个参数是认证信息,即密码,为后面验证可以通过,需要和token中的内容一样
// 第三个参数是领域名称
return new SimpleAuthenticationInfo(token.getPrincipal(), token.getCredentials(), user.getUserName());
return new SimpleAuthenticationInfo(token.getPrincipal(), token.getCredentials(), getName());
}
}
Loading

0 comments on commit 0add169

Please sign in to comment.