1.Shiro架构
Shiro三个主要的概念:
- Subject
- SecurityManager
- Realms
Shiro外部架构
- Subject:主体,当前参与应用安全部分的主体。可以是用户,可以是第三方服务,可以是cron 任务,或者任何东西。主要指一个正在与当前软件交互的东西。所有Subject都需要SecurityManager,当与Subject进行交互,这些交互行为实际上被转换为与SecurityManager的交互
- SecurityManager:安全管理员,Shiro架构的核心,它就像Shiro内部所有原件的保护伞。然而一旦配置了SecurityManager,SecurityManager就用到的比较少,开发者大部分时间都花在Subject上面。当你与Subject进行交互的时候,实际上是SecurityManager在背后帮你举起Subject来做一些安全操作。
- Realms:Realms作为Shiro和应用的连接桥,当需要与安全数据交互的时候,像用户账户,或者访问控制,Shiro就从一个或多个Realms中查找。Shiro提供了一些可以直接使用的Realms,如果默认的Realms不能满足你的需求,你也可以定制自己的Realms
Shiro整体架构
-
Authenticator:认证器,管理登陆与登出。
-
Authorizer:授权器,赋予主体权限。
-
Session Manager:session管理器,session管理机制。不借助任何web容器使用session
-
Session Dao:session操作,主要增删改查。
-
Cache Manager:缓存管理器
-
Pluggable Realms(1 or more):shiro与数据库的连接,认证授权校验
-
Cryptography:数据加密
Shiro核心组件
-
Subject 即主体,外部引用于 Subject 进行交互,Subject 记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。Subject 在 Shrio 中是一个接口,接口中定义了很多认证授权相关的方法,外部程序通过 Sbject 进行认证授权,而 Sbject 是通过 SecurityManager 安全管理器进行认证授权的。
-
SecurityManager 即安全管理器,对全部的 Sbject 进行安全管理,它是 Shrio 的核心,通过 SecurityManager 可以完成全部 Sbject 的认证、授权等。实质上 SecurityManager 是通过 Authenticator 进行认证,通过 Authorizer 进行授权,通过 SessionManager 进行会话管理等。SecurityManager是一个接口,继承了 Authenticator ,Authorizer ,SessionManager 这三个接口。
-
Authenticator即认证器,对用户身份进行认证,Authenticator 是一个接口,Shrio 提供了 ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator 基本上可以满足大多数需求,也可以自定义认证器。
Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。 -
Realm即领域,相当于 datasource 数据源,SecurityManager 进行安全认证需要通过 Realm 获取用户权限数据,比如:如果用户身份数据在数据库,那么 Realm 就需要从数据库获取用户身份信息。不要把 Realm 理解成只是从数据源取数据,在 Realm 中还有认证授权校验的相关代码。
-
SessionManager 即会话管理,Shrio 框架定义了一套会话管理,它不依赖 web 容器的 Session ,所以Shrio 可以使用在非 Web 应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。
-
SessionDAO 即会话DAO,是对 Session 会话操作的一套接口,比如要将 Session 存储到数据库,可以通过 jdbc 将会话存储到数据库。
-
CacheManager 即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。
-
Cryptografy即密码管理,Shrio 提供了一套解密/加密的组件,方便开发。比如提供常用的散列、加/解密功能。
2.Shiro环境搭建
主要分三步
- 创建realm领域对象
- 创建DefaultWebSecurityManager安全管理器
- 创建ShiroFilterFactoryBean 设置安全管理器
1.导入maven
<!-- 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>
2.编写Realm
public class UserRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
return null;
}
}
3.编写ShiroConfig
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean 3
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
return bean;
}
//DefaultWebSecurityManager 2
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
return securityManager;
}
//创建realm对象 1
@Bean(name = "userRealm")
public UserRealm userRealm(){
return new UserRealm();
}
}
3.Shiro用户登录拦截与认证
shiro的内置过滤器各个权限
- anno:无需认证就可以访问
- authc:必须认证了才能访问
- user:必须拥有记住我功能才能访问
- perms:必须拥有对某个资源的权限才能访问
- role:拥有某个角色权限才能访问
在getShiroFilterFactoryBean中添加内置过滤器实现登录拦截
bean.setLoginUrl("/user/toLogin"); 可以设置拦截时的重定向
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
//添加shiro的内置过滤器
/*
anno:无需认证就可以访问
authc:必须认证了才能访问
user:必须拥有记住我功能才能访问
perms:必须拥有对某个资源的权限才能访问
role:拥有某个角色权限才能访问
*/
Map<String, String> filterMap = new LinkedHashMap<>();
//设置添加和修改页面必须认证了才能访问
filterMap.put("/users/add","authc");
filterMap.put("/users/update","authc");
bean.setFilterChainDefinitionMap(filterMap);
//设置如果没有权限就跳转到登录页面
bean.setLoginUrl("/user/toLogin");
return bean;
}
在UserRealm 下的 doGetAuthenticationInfo方法中 添加用户执行认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了认证");
String username = "root";
String password = "123456";
UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
if (!userToken.getUsername().equals(username)){
return null; //抛出异常,UnknownAccountException
}
//密码认证,shiro自己做
return new SimpleAuthenticationInfo("",password,"");
}
在controller中获取用户,封装用户登录的数据和登录认证
@PostMapping("/login")
public String login(String username, String password, Model model){
//获取当前的用户
Subject subject = SecurityUtils.getSubject();
//封装用户的登录数据
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
//执行登录方法,如果说没有异常就说明登录成功
try {
//登陆成功返回首页
subject.login(token);
return "index";
}catch (UnknownAccountException e){
//用户名错误返回登录页
model.addAttribute("msg","用户名错误");
return "login";
}catch (IncorrectCredentialsException e){
//密码错误返回登录页
model.addAttribute("msg","密码错误");
return "login";
}
}