说明:本文主要配合一个简单的项目来讲解shiro的使用,文末会附上项目的github以及码云的地址供下载参考。
shiro是一个轻量级的安全框架,包含用户认证和用户授权
Authentication(认证):用户身份识别,通常被称为用户“登录”
Authorization(授权):访问控制。比如某个用户是否具有某个操作的使用权限。
Session Management(会话管理):特定于用户的会话管理,甚至在非web 或 EJB 应用程序。
Cryptography(加密):在对数据源使用加密算法加密的同时,保证易于使用。
以上四点,被 Shiro 框架的开发团队称之为应用安全的四大基石。
Subject:用户主体(把操作交给SecurityManager)
SecurityManager:安全管理器(管理所有Subject,SecurityManager 是 Shiro 架构的核心,配合内部安全组件共同组成保护伞。)
Reaml:shiro连接数据的桥梁,Realm 本质上是一个特定的安全 DAO,它封装与数据源连接的细节,得到Shiro 所需的相关的数据。就是去操作数据库,查询登录账户、密码、权限等
创建ShiroFilterFactoryBean;(shiro的拦截器,配置哪些路径需要被拦截,哪些不需要被拦截)
创建DefaultWebSecurityManager;(用于设置realm)
创建Reaml(继承AuthorizingReaml);
常用的过滤器:
anno:无需认证(登陆)可以访问
authc:必须认证才能访问
user:如果使用rememberMe的功能可以直接访问
perms:该资源必须得到资源权限可以访问
role:该资源必须得到角色权限才能访问
在使用Shiro的时候,我们先要确定一下我们的步骤:
1.加入Shiro的依赖包
2.实现自己的Realm类(通过继承AuthorizingRealm类);
3.实现Shiro的配置类
4.实现前端的登录界面以及编写Controller类
第一步:在pom.xml中加入依赖包:
<!--添加shiro支持-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
第二步:创建自定义MyRealm类:
我们需要创建自定义的MyRealm类继承AuthorizingRealm重写doGetAuthenticationInfo和 doGetAuthorizationInfo方法。其中 doGetAuthenticationInfo 是用来验证用户身份,doGetAuthorizationInfo 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。
/**
* 自定义Realm
*
*/
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Autowired
private RoleMenuMapper roleMenuMapper;
@Autowired
private MenuMapper menuMapper;
@Autowired
private RoleMapper roleMapper;
/**
* 授权--验证url
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//获取当前登录的用户名
String name = (String) SecurityUtils.getSubject().getPrincipal();
// 有了用户就可以拿到对应的角色,有角色就可以拿到对应的菜单
User user = userService.findByName(name);
// 这个角色对应 的菜单,先用roleId查找角色和菜单的关联表,再用关联表中的menuId查找对应的menu对象,
// 再.getPermissions找出权限名
List<RoleMenu> roleMenuList = roleMenuMapper.findByRoleId(user.getRoleId());//查出所有角色关联的菜单
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
for (RoleMenu roleMenu : roleMenuList) {
info.addStringPermission(menuMapper.findById(roleMenu.getMenuId()).getPermissions());// 添加权限
}
// 设置角色
Set<String> roles = new HashSet<String>();
roles.add(roleMapper.findById(user.getRoleId()).getName());
info.setRoles(roles);
return info;
}
/**
* 权限认证--登录
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String name = (String) token.getPrincipal();// 拿到的是用户名 就是UsernamePasswordTokenr的第一个参数 name
User user = userService.findByName(name);// 根据name找到数据库中的user实体
if (user != null) {// 这里的一步主要是来判断密码是否正确
/**
* 对于传的三个参数
* Object principal:可以传username或者user对象都可以
* Object credentials:用户密码:注意这里是指从数据库中获取的password
* String realmName:即当前realm的名称(个人尝试:随便传个字符串就行,但不能为null,具体不太明白)
*/
return new SimpleAuthenticationInfo(user.getName(), user.getPwd(), "MyRealm");
} else {
return null;
}
}
}
第三步: 创建Shiro的配置类:
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//必须设置设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
//设置没有登录时跳转的页面
//如果不设置默认会自动寻找web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/login");
/**
* 登录成功后要跳转的链接
* 一般shiro验证成功后,我们需要执行一些操作,比如:缓存用户信息到session,返回页面一些信息等操作
* 所以,不需要shiro来跳转
*/
//shiroFilterFactoryBean.setSuccessUrl("/index");
/**
* 添加拦截器:
* 常用内置过滤器
* anon:无需认证(登录)用户可以直接访问(就相当于游客)
* authc:必须要认证才能访问
* user:使用了remenberMe的功能的用户可以直接无需登录访问(相当于记住登录状态)
* perms:必须获取资源权限才能访问
* role:必须获取角色授权才能访问
* logout:用户登出,这里不用设置控制器,退出后直接跳转到/
*/
Map<String,String> filterMap = new LinkedHashMap<String,String>();
//配置不会被拦截的链接 顺序判断 anon:所有的url都可以匿名访问
filterMap.put("/static/**","anon");
filterMap.put("/user/login","anon");
//配置退出过滤器,其中具体的实现shiro已经替我们实现了
filterMap.put("/logout", "logout");
//authc:所有url需要认证才能访问
filterMap.put("/admin/**", "authc");
filterMap.put("/houtai/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}
/**
* 创建 安全管理器 SecurityManager
* @return
*/
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//设置realm
securityManager.setRealm(myRealm());
return securityManager;
}
/**
* 身份认证realm;(这个需要自己写,账号密码校验,权限等)
* @return
*/
@Bean
public MyRealm myRealm(){
return new MyRealm();
}
/**
* shiro生命周期处理器
* @return
*/
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* 开启shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助springAOP扫描使用
* shiro注解的类,并在必要时进行安全逻辑验证
* 配置以下两个bean即可实现此功能
* @return
*/
@Bean
@DependsOn({"lifecycleBeanPostProcessor"})
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
return authorizationAttributeSourceAdvisor;
}
/**
* 配置 ShiroDialect 用于thymeleaf 和shiro标签配合使用
* @return
*/
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
}
第四步: 实现前端的登录界面以及编写Controller类:
因为界面、类什么得项目中都有,注释也比较全面,这里就不在展示了。
项目地址:
https://github.com/isczy/BookSystem.git
1.启动项目时,就会加载ShiroConfig配置类
2.输入用户名密码,点击登录后有 subject.login(token);
的操作,其会自动委托给 Security Manager,调用之前必须通过 SecurityUtils.setSecurityManager() 设置
3.SecurityManager 负责真正的身份验证逻辑;它会委托给 Authenticator 进行身份验证
4.Authenticator 会把相应的 token 传入 Realm,从 Realm 获取身份验证信息,如果没有返回 / 抛出异常表示身份验证失败了。此处可以配置多个 Realm,将按照相应的顺序及策略进行访问。
5.执行自定义Realm中重写的doGetAuthenticationInfo方法,进行验证用户名和密码。正常执行完返回,就说明验证成功,。否则抛异常:UnknownAccountException:用户名不存在/IncorrectCredentialsException:密码错误
4.访问项目中的地址时,会执行自定义Realm中重写的doGetAuthorizationInfo方法,进行url验证授权,看用户是否有该权限
项目地址:
github仓库地址:https://github.com/isczy/BookSystem.git
码云地址:https://gitee.com/isczy/BookSystem.git