一.shiro基本介绍
1、Shiro是Apache下的一个开源项目,我们称之为Apache Shiro。它是一个很易用与Java项目的的安全框架,提供了认证、授权、加密、会话管理,与spring Security 一样都是做一个权限的安全框架,但是与Spring Security 相比,在于 Shiro 使用了比较简单易懂易于使用的授权方式。shiro属于轻量级框架,相对于security简单的多,也没有security那么复杂。所以我这里也是简单介绍一下shiro的使用。
2、非常简单;其基本功能点如下图所示:
Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
Web Support:Web支持,可以非常容易的集成到Web环境;
Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
Testing:提供测试支持;
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。
High-Level Overview 高级概述
在概念层,Shiro 架构包含三个主要的理念:Subject,SecurityManager和 Realm。下面的图展示了这些组件如何相互作用,我们将在下面依次对其进行描述。
- Subject:当前用户,Subject 可以是一个人,但也可以是第三方服务、守护进程帐户、时钟守护任务或者其它–当前和软件交互的任何事件。
- SecurityManager:管理所有Subject,SecurityManager 是 Shiro 架构的核心,配合内部安全组件共同组成安全伞。
- Realms:用于进行权限信息的验证,我们自己实现。Realm 本质上是一个特定的安全 DAO:它封装与数据源连接的细节,得到Shiro 所需的相关的数据。在配置 Shiro 的时候,你必须指定至少一个Realm 来实现认证(authentication)和/或授权(authorization)。
我们需要实现Realms的Authentication 和 Authorization。其中 Authentication 是用来验证用户身份,Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。
记住一点,Shiro不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过相应的接口注入给Shiro即可。
二.配置信息
1.添加maven依赖
org.apache.shiro shiro-core 1.4.0 org.apache.shiro shiro-spring 1.4.0
2.Shiro配置类
@Configuration
public class ShiroConfigBean {
//配置自定义的权限登录器
@Bean
public MyShiroRealm myShiroRealm() {
MyShiroRealm myShiroRealm = new MyShiroRealm();
return myShiroRealm;
}
//session管理器
@Bean("sessionManager")
public SessionManager sessionManager(){
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionValidationSchedulerEnabled(true);
sessionManager.setSessionIdCookieEnabled(true);
return sessionManager;
}
//配置核心安全事务管理器
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
securityManager.setSessionManager(sessionManager);
return securityManager;
}
/**
* 注册DelegatingFilterProxy(Shiro)
* 集成Shiro有2种方法:
* 1. 按这个方法自己组装一个FilterRegistrationBean(这种方法更为灵活,可以自己定义UrlPattern,
* 在项目使用中你可能会因为一些很但疼的问题最后采用它, 想使用它你可能需要看官网或者已经很了解Shiro的处理原理了)
* 2. 直接使用ShiroFilterFactoryBean(这种方法比较简单,其内部对ShiroFilter做了组装工作,无法自己定义UrlPattern,
* 默认拦截 /*)
*
*/
// @Bean
// public FilterRegistrationBean filterRegistrationBean() {
// FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
// filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
// // 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
// filterRegistration.addInitParameter("targetFilterLifecycle", "true");
// filterRegistration.setEnabled(true);
// filterRegistration.addUrlPatterns("/*");// 可以自己灵活的定义很多,避免一些根本不需要被Shiro处理的请求被包含进来
// return filterRegistration;
// }
//Filter工厂,设置对应的过滤条件和跳转条件
@Bean
public ShiroFilterFactoryBean shirFilter(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 拦截器,必须用LinkedHashMap添加拦截规则
Map filterChainDefinitionMap = new LinkedHashMap();
// 设置login URL
shiroFilterFactoryBean.setLoginUrl("/login");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/LoginSuccess.action");
// 未授权的页面
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized.action");
// src="jquery/jquery-3.2.1.min.js" 生效
filterChainDefinitionMap.put("/jquery/*", "anon");
// 设置登录的URL为匿名访问,因为一开始没有用户验证
filterChainDefinitionMap.put("/login.action", "anon");
filterChainDefinitionMap.put("/Exception.class", "anon");
// 我写的url一般都是xxx.action,根据你的情况自己修改
filterChainDefinitionMap.put("/*.action", "authc");
// 退出系统的过滤器
filterChainDefinitionMap.put("/logout", "logout");
// 现在资源的角色
filterChainDefinitionMap.put("/admin.html", "roles[admin]");
// filterChainDefinitionMap.put("/user.html", "roles[user]");
// 最后一班都,固定格式
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/*
* 开启shiro aop注解支持 使用代理方式;所以需要开启代码支持;
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new
AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* DefaultAdvisorAutoProxyCreator,Spring的一个bean,由Advisor决定对哪些类的方法进行AOP代理。
*/
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
}
Shiro 的默认Filter工厂 对应的类
Filter名称 | 类路径(点击可以进入官方介绍,强烈建议看看 ) |
anon | org.apache.shiro.web.filter.authc.AnonymousFilter |
authc | org.apache.shiro.web.filter.authc.FormAuthenticationFilter |
authcBasic | org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter |
logout | org.apache.shiro.web.filter.authc.LogoutFilter |
noSessionCreation | org.apache.shiro.web.filter.session.NoSessionCreationFilter |
perms | org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter |
port | org.apache.shiro.web.filter.authz.PortFilter |
rest | org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter |
roles | org.apache.shiro.web.filter.authz.RolesAuthorizationFilter |
ssl | org.apache.shiro.web.filter.authz.SslFilter |
user | org.apache.shiro.web.filter.authc.UserFilter |
3.自定义Realm类
public class MyShiroRealm extends AuthorizingRealm {
/**
* 认证(验证当前用户信息)
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 将AuthenticationToken强转为AuthenticationToken对象
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
// 获得从表单传过来的用户名
String username = upToken.getUsername();
// 从数据库查看是否存在用户
UserService userService = new UserService();
// 如果用户不存在,抛此异常
if (!userService.selectUsername(username)) {
throw new UnknownAccountException("无此用户名!");
}
// 认证的实体信息,可以是username,也可以是用户的实体类对象,这里用的用户名
Object principal = username;
// 从数据库中查询的密码
Object credentials = userService.selectPassword(username);
// 颜值加密的颜,可以用用户名
ByteSource credentialsSalt = ByteSource.Util.bytes(username);
// 当前realm对象的名称,调用分类的getName()
String realmName = this.getName();
// 创建SimpleAuthenticationInfo对象,并且把username和password等信息封装到里面
// 用户密码的比对是Shiro帮我们完成的
SimpleAuthenticationInfo info = null;
info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
return info;
}
// 授权(验证权限时调用)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// User user=(User)
// principals.fromRealm(this.getClass().getName()).iterator().next();//获取session中的用户
// System.out.println("在MyShiroRealm中AuthorizationInfo(授权)方法中从session中获取的user对象:"+user);
// 从PrincipalCollection中获得用户信息
Object principal = principals.getPrimaryPrincipal();
System.out.println("ShiroRealm AuthorizationInfo:" + principal.toString());
// 根据用户名来查询数据库赋予用户角色,权限(查数据库)
Set roles = new HashSet<>();
Set permissions = new HashSet<>();
roles.add("user");
permissions.add("user:query");
if ("admin".equals(principal)) {
roles.add("admin");
permissions.add("admin:query");
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
//天加权限
info.setStringPermissions(permissions);
return info;
// return null;
}
}
- doGetAuthenticationInfo执行时机如下
(1).当调用Subject currentUser = SecurityUtils.getSubject();
currentUser.login(token);
- doGetAuthorizationInfo执行时机有三个,如下:
(1).subject.hasRole(“admin”) 或 subject.isPermitted(“admin”):自己去调用这个是否有什么角色或者是否有什么权限的时候;
(2).@RequiresRoles("admin") :在方法上加注解的时候;
(3).[@shiro.hasPermission name = "admin"][/@shiro.hasPermission]:在页面上加shiro标签的时候,即进这个页面的时候扫描到有这个标签的时候。