shiro architecture
摘自apache官网:https://shiro.apache.org/architecture.html
-
Subject (org.apache.shiro.subject.Subject) A security-specific ‘view’ of the entity (user, 3rd-party service, cron job, etc) currently interacting with the software. -
SecurityManager (org.apache.shiro.mgt.SecurityManager) As mentioned above, the SecurityManager is the heart of Shiro’s architecture. It is mostly an ‘umbrella’ object that coordinates its managed components to ensure they work smoothly together. It also manages Shiro’s view of every application user, so it knows how to perform security operations per user. -
Authenticator (org.apache.shiro.authc.Authenticator) The Authenticator is the component that is responsible for executing and reacting to authentication (log-in) attempts by users. When a user tries to log-in, that logic is executed by the Authenticator. The Authenticator knows how to coordinate with one or more Realms that store relevant user/account information. The data obtained from these Realms is used to verify the user’s identity to guarantee the user really is who they say they are. -
Authentication Strategy (org.apache.shiro.authc.pam.AuthenticationStrategy) If more than one Realm is configured, the AuthenticationStrategy will coordinate the Realms to determine the conditions under which an authentication attempt succeeds or fails (for example, if one realm succeeds but others fail, is the attempt successful? Must all realms succeed? Only the first?). -
Authorizer (org.apache.shiro.authz.Authorizer) The Authorizer is the component responsible determining users’ access control in the application. It is the mechanism that ultimately says if a user is allowed to do something or not. Like the Authenticator, the Authorizer also knows how to coordinate with multiple back-end data sources to access role and permission information. The Authorizer uses this information to determine exactly if a user is allowed to perform a given action. -
SessionManager (org.apache.shiro.session.mgt.SessionManager) The SessionManager knows how to create and manage user Session lifecycles to provide a robust Session experience for users in all environments. This is a unique feature in the world of security frameworks - Shiro has the ability to natively manage user Sessions in any environment, even if there is no Web/Servlet or EJB container available. By default, Shiro will use an existing session mechanism if available, (e.g. Servlet Container), but if there isn’t one, such as in a standalone application or non-web environment, it will use its built-in enterprise session management to offer the same programming experience. The SessionDAO exists to allow any datasource to be used to persist sessions. -
SessionDAO (org.apache.shiro.session.mgt.eis.SessionDAO) The SessionDAO performs Session persistence (CRUD) operations on behalf of the SessionManager. This allows any data store to be plugged in to the Session Management infrastructure. -
CacheManager (org.apache.shiro.cache.CacheManager) The CacheManager creates and manages Cache instance lifecycles used by other Shiro components. Because Shiro can access many back-end data sources for authentication, authorization and session management, caching has always been a first-class architectural feature in the framework to improve performance while using these data sources. Any of the modern open-source and/or enterprise caching products can be plugged in to Shiro to provide a fast and efficient user-experience. -
Cryptography (org.apache.shiro.crypto.*) Cryptography is a natural addition to an enterprise security framework. Shiro’s crypto package contains easy-to-use and understand representations of crytographic Ciphers, Hashes (aka digests) and different codec implementations. All of the classes in this package are carefully designed to be very easy to use and easy to understand. Anyone who has used Java’s native cryptography support knows it can be a challenging animal to tame. Shiro’s crypto APIs simplify the complicated Java mechanisms and make cryptography easy to use for normal mortal human beings. -
Realms (org.apache.shiro.realm.Realm) As mentioned above, Realms act as the ‘bridge’ or ‘connector’ between Shiro and your application’s security data. When it comes time to actually interact with security-related data like user accounts to perform authentication (login) and authorization (access control), Shiro looks up many of these things from one or more Realms configured for an application. You can configure as many Realms as you need (usually one per data source) and Shiro will coordinate with them as necessary for both authentication and authorization.
以上说明:
1、SecurityManager 是shiro架构的核心,使用示例之一:在shiro的配置类中
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
CustomisedAuthenticator customisedAuthenticator = new CustomisedAuthenticator();
customisedAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
securityManager.setAuthenticator(customisedAuthenticator);
CustomisedAuthorizer customisedAuthorizer = new CustomisedAuthorizer();
securityManager.setAuthorizer(customisedAuthorizer);
List<Realm> realms = new ArrayList<Realm>();
realms.add(getUserRealm());
realms.add(getAnotherRealm());
securityManager.setRealms(realms);
return securityManager;
}
2、Authenticator 用于账号/密码的认证
3、Authorizer 用于授权角色roles/权限privileges
4、Realms :As mentioned above, Realms act as the ‘bridge’ or ‘connector’ between Shiro and your application’s security data. realm是连接shiro和应用数据安全的桥梁。开发者可在realm中完成Authenticator 和Authorizer 的业务实现。 You can configure as many Realms as you need (usually one per data source) and Shiro will coordinate with them as necessary for both authentication and authorization. 只要需要可以配置一个或多个Realm,shiro在必要时会为认证和授权提供协调处理。
多Realm的情况需要注意一下,见后文 realm示例
package com.xl.practice.springbootshiropractice.shiro;
import java.util.Set;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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 com.xl.practice.springbootshiropractice.entity.User;
import com.xl.practice.springbootshiropractice.service.UserService;
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("=================================== UserRealm的授权方法 ===================================");
UserLogin userLogin = (UserLogin) principalCollection.getPrimaryPrincipal();
String name = userLogin.getUsername();
Set<String> privileges = userService.listPrivilege(name);
Set<String> roles = userService.listRole(name);
SimpleAuthorizationInfo s = new SimpleAuthorizationInfo();
s.setStringPermissions(privileges);
s.setRoles(roles);
return s;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String name = token.getPrincipal().toString();
User user = userService.selectUserByName(name);
String passwordInDB = user.getPassword();
SimpleAuthenticationInfo a = new SimpleAuthenticationInfo(token, passwordInDB, null, getName());
return a;
}
}
shiro官方手册:https://shiro.apache.org/reference.html 截图:
demo
springboot与shiro基础
流程介绍
以下为shiro认证过程的简要类图关系
说明
由上图可知,如需完成shiro认证功能的集成,只需设计3个类(不包括shiro的配置类见下文),其中只有一个是必须的,其余两个根据业务需求而定。
1、Subject的login方法的参数类:AuthenticationToken (非必须) 如果业务上需要自定义字段/方法,则可以设计一个自定义的Token继承Shiro的UsernamePasswordToken,如上图中的UserLogin。如果不需要则直接将UsernamePasswordToken作为Subject.login的参数即可:将用户名/密码封装到UsernamePasswordToken。
2、自定义的RealmAuthenticator(非必须) 如果系统中有多个Realm,并且在认证的时候需要确定用具体的那一个或哪几个Realm,则需要自定义一个RealmAuthenticator,如上图中的CustomisedAuthenticator,继承自ModularRealmAuthenticator,并且,这类需要在shiro的配置类中进行显示的配置。如果不需要则shiro会使用默认的ModularRealmAuthenticator,根据源码可知默认的实现会使用所有的Realm。
3、自定义Realm(必须) 自定义的Realm是必须要的,因为需要在这个类里面实现具体的业务认证(授权也是在这个类里面实现的),从shiro的设计中也可看出: 自定义的Realm需要继承AuthenticatingRealm而AuthenticatingRealm中的认证方法是抽象方法,也就是说必须要有用户自定义的类来继承并实现之。
以下为shiro授权过程的简要类图关系
说明
由上图可知,要完成shiro授权的集成只需要设计2个类即可,一个必须,一个非必须,但是注意授权的前提是完成认证。
1、自定义的RealmAuthorizer类(非必须) 如果系统中有多个Realm,并且在授权的时候需要确定用具体的那一个或哪几个Realm,则需要自定义一个RealmAuthorizer,如上图中的CustomisedAuthorizer,继承自ModularRealmAuthorizer,并且,本类需要在shiro的配置类中进行显示的配置。如果不需要则shiro会使用默认的ModularRealmAuthorizer,根据源码可知默认的实现会使用所有的Realm。
2、自定义Realm(必须) 自定义的Realm是必须要的,因为需要在这个类里面实现具体的业务授权(认证也是在这个类里面实现的),从shiro的设计中也可看出: 自定义的Realm需要继承AuthorizingRealm而AuthorizingRealm中的认证方法是抽象方法,也就是说必须要有用户自定义的类来继承并实现之。
引入依赖
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-web -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.6.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.6.0</version>
</dependency>
编写shiro配置类
package com.xl.practice.springbootshiropractice.shiro;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.Filter;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ShiroConfiguration {
@Bean
public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
autoProxyCreator.setProxyTargetClass(true);
return autoProxyCreator;
}
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
System.out.println("ShiroConfiguration.shirFilter()");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setLoginUrl("/notLogin");
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
Map<String, Filter> customisedFilter = new HashMap<>();
customisedFilter.put("url", new CustomisedURLPathMatchingFilter());
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/**", "authc");
filterChainDefinitionMap.put("/**", "url");
shiroFilterFactoryBean.setFilters(customisedFilter);
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
CustomisedAuthenticator customisedAuthenticator = new CustomisedAuthenticator();
customisedAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
securityManager.setAuthenticator(customisedAuthenticator);
CustomisedAuthorizer customisedAuthorizer = new CustomisedAuthorizer();
securityManager.setAuthorizer(customisedAuthorizer);
List<Realm> realms = new ArrayList<Realm>();
realms.add(getUserRealm());
realms.add(getAnotherRealm());
securityManager.setRealms(realms);
return securityManager;
}
@Bean
public UserRealm getUserRealm() {
UserRealm myShiroRealm = new UserRealm();
myShiroRealm.setAuthenticationCachingEnabled(true);
myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myShiroRealm;
}
@Bean
public AnotherRealm getAnotherRealm() {
AnotherRealm anotherRealm = new AnotherRealm();
anotherRealm.setAuthenticationCachingEnabled(true);
anotherRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return anotherRealm;
}
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");
return hashedCredentialsMatcher;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
自定义Token类
package com.xl.practice.springbootshiropractice.shiro;
import org.apache.shiro.authc.UsernamePasswordToken;
public class UserLogin extends UsernamePasswordToken {
private String type;
public UserLogin() {
super();
}
public UserLogin(String type,String username,String password) {
super(username,password);
this.type = type;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
自定义Realm
第一个realm
package com.xl.practice.springbootshiropractice.shiro;
import java.util.Set;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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 com.xl.practice.springbootshiropractice.entity.User;
import com.xl.practice.springbootshiropractice.service.UserService;
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("=================================== UserRealm的授权方法 ===================================");
UserLogin userLogin = (UserLogin) principalCollection.getPrimaryPrincipal();
String name = userLogin.getUsername();
Set<String> privileges = userService.listPrivilege(name);
Set<String> roles = userService.listRole(name);
SimpleAuthorizationInfo s = new SimpleAuthorizationInfo();
s.setStringPermissions(privileges);
s.setRoles(roles);
return s;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String name = token.getPrincipal().toString();
User user = userService.selectUserByName(name);
String passwordInDB = user.getPassword();
SimpleAuthenticationInfo a = new SimpleAuthenticationInfo(token, passwordInDB, null, getName());
return a;
}
}
第二个Realm
package com.xl.practice.springbootshiropractice.shiro;
import java.util.HashSet;
import java.util.Set;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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 com.xl.practice.springbootshiropractice.entity.User;
import com.xl.practice.springbootshiropractice.service.UserService;
public class AnotherRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("+++++++++++++++++++++++++++++ AnotherRealm的授权方法 +++++++++++++++++++++++++++++");
SimpleAuthorizationInfo s = new SimpleAuthorizationInfo();
Set<String> privileges = new HashSet<String>();
Set<String> roles = new HashSet<String>();
UserLogin userLogin = (UserLogin) principals.getPrimaryPrincipal();
String type = userLogin.getType();
privileges.add("AAA");
s.setRoles(roles);
s.setStringPermissions(privileges);
return s;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String name = token.getPrincipal().toString();
User user = userService.selectUserByName(name);
String passwordInDB = user.getPassword();
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(token, passwordInDB, null, getName());
return simpleAuthenticationInfo;
}
}
自定义认证的RealmAuthenticator
package com.xl.practice.springbootshiropractice.shiro;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
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 CustomisedAuthenticator extends ModularRealmAuthenticator {
@Override
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)
throws AuthenticationException {
System.out.println("UserModularRealmAuthenticator:method doAuthenticate() execute ");
assertRealmsConfigured();
UserLogin userLogin = (UserLogin) authenticationToken;
String type = userLogin.getType();
Collection<Realm> realms = getRealms();
List<Realm> typeRealms = new ArrayList<>();
for (Realm realm : realms) {
System.out.println(realm.getName());
if (realm.getName().contains(type)) {
typeRealms.add(realm);
}
}
if (typeRealms.size() == 1){
System.out.println("doSingleRealmAuthentication() execute ");
return doSingleRealmAuthentication(typeRealms.get(0), userLogin);
}
else{
System.out.println("doMultiRealmAuthentication() execute ");
return doMultiRealmAuthentication(typeRealms, userLogin);
}
}
}
自定义授权的RealmAuthorizer
package com.xl.practice.springbootshiropractice.shiro;
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 CustomisedAuthorizer extends ModularRealmAuthorizer {
@Override
public boolean hasRole(PrincipalCollection principals, String roleIdentifier) {
assertRealmsConfigured();
UserLogin userLogin = (UserLogin) principals.getPrimaryPrincipal();
String type = userLogin.getType();
for (Realm realm : getRealms()) {
if (!(realm instanceof Authorizer)) continue;
if (realm.getName().contains(type)) {
if (((Authorizer) realm).hasRole(principals, roleIdentifier)) {
return true;
}
}
}
return false;
}
}
问题
相关文章
Springboot整合shiro:第一篇 用户验证
源文件
shiro授权类图关系简化版 shiro认证 类图关系简化版 demo项目源码
|