什么是Shiro权限管理?
Apache Shiro 是 Java 的一个安全框架。目前,使用 Apache Shiro 的人越来越多,因为它相 当简单,对比
Spring Security,可能没有 Spring Security 做的功能强大,但是在实际工作时
可能并不需要那么复杂的东西,所以使用小而简单的 Shiro 就足够了。对于它俩到底哪个 好,这个不必纠结,能更简单的解决项目问题就好了。
权限管理分为认证和授权
认证
认证的基本概念
身份认证:校验用户是不是系统合法用户的过程(比如登录操作)
身份认证,就是判断一个用户是否为合法用户的处理过程。
最常用的简单身份认证方式是系统通过核对用户输入的用户名和密码,看其是否与系统中存储的该用户的用户名和密码是否一致,来判断用户身份是否正确。
对于采用指纹等系统,则出示指纹;对于硬件Key等刷卡系统,则需要刷卡。通过二维码等等都是认证的过程
认证基本的对象
Subject 主体
官方给出的解释是这样的:
Without question, the most important concept in Apache Shiro is the Subject. ‘Subject’ is just a security term that means a security-specific ‘view’ of an application user. A Shiro Subject instance represents both security state and operations for a single application user.
翻译过来就是:
毫无疑问,shiro中最重要的概念就是subject(主体)。subject是一个虚拟的用户对象(就是相当于我们之前的User Admin对象等)。
使用Subject的之后的流程变化
其实,
Subject可以认为就是Admin User这些类
另外subject中还有两个重要概念:
Credential 凭证信息 就是密码的意思
Principal 身份信息 就是账号的意思
扩展阅读:(看一下官方的解释为什么叫Subject而不叫User)
We originally wanted to call it ‘User’ since that “just makes sense”, but we decided against it: too many applications have existing APIs that already have their own User classes/frameworks, and we didn’t want to conflict with those. Also, in the security world, the term ‘Subject’ is actually the recognized nomenclature.
翻译如下:
我们最初是想把它叫做User的,这样很容易理解,但是大部分的应用程序都已经有了User类,为了不和这些原有的api发生冲突,我们最后决定不这么做。
认证的登录操作案例:SpringBoot集成shiro
下面开始使用Shiro实现带认证的登录操作
导入依赖
<!--shiro相关依赖--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.3.2</version> </dependency>
第一个demo
基本思路
用户输入的账号密码 和 数据库中存储的账号密码 比对
注意:shiro 默认不支持连接数据库 默认通过配置文件获取数据
所以我们要重写Realm中的方法,实现对数据库的连接
- 接收用户输入的账号密码,
- 要重写Realm中的方法,实现对数据库的连接和数据获取
- 写Springboot的配置类,创建并将重写的Realm设置放进去安全管理器, 将安全管理器 和Subject建立联系
- 将用户输入的账号密码给Subject
- 调用Subject的login方法完成登录
代码实现流程
重写Realm的代码
package com.macw.realm; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.macw.entity.Admin; import com.macw.mapper.AdminMapper; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.realm.AuthenticatingRealm; import org.apache.shiro.subject.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Resource; /** * @author maCw * @version 1.0 * @date 2019/8/22 16:13 */ public class MyRealm extends AuthenticatingRealm { @Resource private AdminMapper adminMapper; /** * 记录日志 */ Logger logger = LoggerFactory.getLogger(getClass()); @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //1,获取令牌中的数据,账号 UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; String username = token.getUsername(); //2,通过账号查询获取数据库中对应的账号信息 Admin admin = adminMapper.selectOne(new QueryWrapper<Admin>().eq("username", username)); logger.info("---"+admin); Subject subject = SecurityUtils.getSubject(); //将查询的对象放进去subject自带的session中去 subject.getSession().setAttribute("admins", admin); //如果有数据,对象为非null,说明查询到了数据,封装account返回 if (admin != null){ SimpleAccount simpleAccount = new SimpleAccount(admin.getUsername(), admin.getPassword(), this.getName()); return simpleAccount; }else { //如果对象为空,return null就会抛出账户不存在异常 return null; } } }
在SpringBoot中配置shiro
package com.macw.config; import com.macw.realm.MyRealm; import org.apache.shiro.mgt.DefaultSecurityManager; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.config.WebIniSecurityManagerFactory; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Map; /** * @Configuration 标记当前类为配置类 相当于spring.xml */ @Configuration public class ShiroConfig { /** * @Bean 声明创建对象 并把对象放在工厂中 相当于bean标签 * 如果形参类型对应的对象在工厂中有 会自动装配上 * @return */ @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultSecurityManager defaultSecurityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); /** * 过滤器链 过滤拦截规则 哪些页面拦截 哪些页面不拦截 */ Map map = new HashMap(); /** * anon 代表匿名可访问 就是不用登录就可以访问 登录页面 登录的url * authc 认证可访问 代表登录后才能访问 * * 支持通配符* * 注意拦截规则 一个一个配置 */ map.put("/login.jsp","anon"); map.put("/login/*","anon"); map.put("/main/*", "authc"); map.put("/guru/*", "authc"); map.put("/menu/*", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(map); /** * 设置安全管理器,将创建的安全管理器放进shiroFilterFactoryBean过滤工厂里面 */ shiroFilterFactoryBean.setSecurityManager(defaultSecurityManager); return shiroFilterFactoryBean; } /** * 创建安全管理器,并将自定义的Realm放进去管理器 * @return */ @Bean public DefaultSecurityManager getDefaultSecurityManager(MyRealm myRealm){ DefaultSecurityManager defaultSecurityManager = new DefaultWebSecurityManager(); // 需要赋值一个Realm defaultSecurityManager.setRealm(myRealm); return defaultSecurityManager; } /** * 创建自定义的Realm */ @Bean public MyRealm getMyRealm(){ return new MyRealm(); } }
如果引用到其他项目的话这里需要修改的是 拦截规则 和 自定义的Realm
修改原来的登录方法
在控制器里的登录方法中修改:
/** * 使用shiro登录 * @param username * @param password * @return */ @RequestMapping("adminLogin") public String adminLogin(String username,String password){ // 1.将用户输入的账号密码 封装在token中 UsernamePasswordToken token = new UsernamePasswordToken(username, password); // 2.获取Subject Subject subject = SecurityUtils.getSubject(); // 3.通过Subject 的login方法 完成登录 try { subject.login(token); //到这里如果没有异常说明登录成功, return "redirect:/main/main.jsp"; }catch (Exception e){ //有异常说明登录失败,重定向到登录页面 return "redirect:/login.jsp"; } }
shiro中Session的使用
方案1:HttpSession
方案2:shiro中的session
Session session1 = SecurityUtils.getSubject().getSession();
注意:方案1 和 方案2 都可以 使用 但是只能使用一种方案 不要混合使用
登录认证总结
认证:登录,身份认证,校验用户是不是系统合法用户的过程
主体Subject:就是Admin,User这些类,但是和之前自己定义的User Admin稍以后区别,Subject不仅封装用户和认证相关的数据(账号密码),还封装了和认证相关的方法(login方法)
Credential 凭证信息 就是密码的意思
Principal 身份信息 就是账号的意思
重写Realm中的方法
为什么要重写Realm中的方法
默认不支持连接数据库 默认的实现是查询配置文件
解决方案
shiro默认的doGetAuthenticationInfo方法是查询配置文件,由于这个方法是父类的一个抽象方法,通过继承和多态,可以继承父类,覆盖这个方法(在方法中写入我们新的方法实现 连接数据库 查询数据库中的账号信息)
集成项目的基本流程
- 写shiro的配置类(创建对象)
注意:拦截规则 和 自定义的Realm 需要根据自己项目的情况调整 - 修改原来的登录方法