shiro认证和授权
啥叫权限?
程序里面的权限是啥?
就是访问路径,路径在程序中对应的就是某种能能力和资源。
如何控制权限显示呢?
权限不应该属于某一个实体,而是属于某一个角色,只不过这个是题拥有了这个角色,而拥有了
这个权限
就是RBAC权限管理思想,不是直接将权限交给用户,而是给用户分配角色,角色赋予用户相应的权限
这样大大地减少了系统的开销
基于RBAC思想进行权限的控制显示
2.搭建web项目(ssm+layUI+freemarker)
RBAC思想:基于角色的权限的控制
什么是权限:就是访问一个资源和操作的某个资源的资格,权限
代码中的权限:一个具体的资源路径
项目中添加权限管理:
- 不同的账号有不同的权限显示
- 访问之前要验证,当前账号是否有权限
基于RBAC思想
五张表:
用户表
用户角色表
角色表
角色权限表
权限表
三个表之间多对多关系
一、认证和授权的含义
一个完整的权限控制应该包含两个流程
- 认证(Authentication):就是检查账号和密码的过程。
- 授权(Authorization):当前账号是否包含相应的角色和权限的过程。
注意 :
授权发生在认证之后,因为授权需要认证的通过
认证:Authentication
授权:Authorrization
二、shiro框架介绍
1. 啥是shiro
是一个基于RBAC思想的权限管理框架
Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication,
authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can
quickly and easily secure any application – from the smallest mobile applications to the largest web
and enterprise applications.2.shiro能做什么
帮助我们进行认证、授权、密码管理和会话管理
3.shiro组成部分
用户-----》账号和密码------》shiro内部验证(
1.用户提供的账号和密码需要验证
2.查找正确的信息
3.进行验证)
三部分:
- 获取用户信息:subject
- 验证功能SecurityManager(认证(Authenticator)、授权(authorizer)、密码(cryptography)、会话(sessionManager))
- 获取正确的信息功能realm
两个获取一个验证
4.shiro执行流程
subject获取用户输入-------》SecurityManager拿subject和realm获取到的数据验证用户的输入信息的正确性《-----realm获真实的用户
三、shiro认证
就是验证账号和密码
使用
1. simpleAccountRealm
第一步导入依赖
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.4.1</version> </dependency>
//获取用户名和密码账号和密码 Scanner scanner = new Scanner(System.in); System.out.println("请输入账号"); String account = scanner.nextLine(); System.out.println("请输入密码"); String password = scanner.nextLine(); /** * todo 1:创建一个realm * SimpleAccountRealm */ SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm(); simpleAccountRealm.addAccount("root", "111"); simpleAccountRealm.addAccount("admin", "222"); /** * todo 2:创建securityManager并传入realm * 传入manager */ DefaultSecurityManager securityManager = new DefaultSecurityManager(simpleAccountRealm); securityManager.setRealm(simpleAccountRealm); SecurityUtils.setSecurityManager(securityManager); /** *todo 3: 获取subject *TODO 并获取密码令牌传入用户和密码 */ Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(account,password); /** * todo 验证 */ try { subject.login(usernamePasswordToken); System.out.println("验证成功"); } catch (AuthenticationException e) { e.printStackTrace(); System.out.println("验证失败"); }
simpleAccountRealm的优缺点
优点:
几乎没有优点
缺点:
- 只能输入账号和密码,以及角色,无法授权
- 一次缓存所有账号信息
2. iniRealm:
IniRealm就是就是存.ini 配置文件中加载进来用户账号和密码,角色权限的信息用来对账户进行认证和授权
方案一
配置文件
[main] #securityManager=org.apache.shiro.mgt.DefaultSecurityManager #securityManager = new DefaultSecurityManager(); #想在配置文件中初始化jdbcrealm 初始化连接池!以及修改jdbcrealm默认sql macther=org.apache.shiro.authc.credential.HashedCredentialsMatcher macther.hashAlgorithmName=md5 macther.hashIterations=108 md5realm=com.itqf.md5.Md5Realm md5realm.credentialsMatcher=$macther securityManager.realm=$md5realm dataSource=com.mchange.v2.c3p0.ComboPooledDataSource dataSource.jdbcUrl=jdbc:mysql:///shirotest dataSource.driverClass=com.mysql.jdbc.Driver dataSource.user=root dataSource.password=root jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm jdbcRealm.authenticationQuery=select lundanzi from user where ergouzi = ? jdbcRealm.dataSource=$dataSource securityManager.realm=$jdbcRealm [users] root=123 admin=111
//创建Realm IniRealm iniRealm = new IniRealm("classpath:my.ini"); DefaultSecurityManager securityManager = new DefaultSecurityManager(); securityManager.setRealm(iniRealm); SecurityUtils.setSecurityManager(securityManager);
方案二:
//创建Realm IniSecurityManagerFactory iniSecurityManagerFactory = new IniSecurityManagerFactory("classpath:my.ini"); SecurityManager securityManager = iniSecurityManagerFactory.getInstance(); SecurityUtils.setSecurityManager(securityManager);
数据来源是数据库,账号密码以及权限角色
优点:
自动中查找账号信息,符合开发场景
缺点:
sql语句固定,也就是说表和字段名字固定—表明必须要叫users
里面要有password 和 username
- 自定义realm
数据可以来自任意位置
创建类继承
一、继承AuthorizingRealm
他可以用来认证账户和授权账户
@Autowired private UserService userService; @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { User user = (User) principals.getPrimaryPrincipal(); Set<String> roles = userService.selectRolesByUsername(user.getUsername()); System.out.println("roles = " + roles); Set<String> permissions = userService.selectPermisssionRoles(roles); System.out.println("permissions = " + permissions); SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); simpleAuthorizationInfo.setRoles(roles); simpleAuthorizationInfo.setStringPermissions(permissions); return simpleAuthorizationInfo; } //方法二 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { //第一步获取用户输入帐号 String username = (String) token.getPrincipal(); //第二部根据用户名查找用户 User user = userService.selectUserByUsername(username); if (user == null) { //账号不存在 return null; }else { SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user,user.getPassword(),"myrealm"); return simpleAuthenticationInfo;//返回查询到的数据 } }
二、继承AuthenticatingRealm
他只能对用户进行认证
它里面只有方法二,对用户进行认证
输入的账号
根据账号查询数据
根据产寻得数据判断,返回数据
return null 账号不存在 不为空返回密码 数据对象
总结subject
subject获取用户数据
认证login()
之后subject 获取的是realm返回的数据
subject.getPrincipal();获取自定义realm嘻哈寻到的真实登陆的账号
subject.isAuthenticated();是否认证
subject.login() 认证
四、shiro授权
授权:就是认证后的账号进行角色和权限的验证
授权一定发生在认证的后边
1、角色授权
- subject.hasRole()|hasAllRoles|hasRoles返回true或者false 如果true就直接放行,否则跳转到
对应的提示页面
- subject.checkRole|checkedRoles()
没有角色就直接抛异常,进行补获,也面条装就行
2、权限授权
授权只有check方法没有has方法
subject.checkPermiision()|checkPermissions()捕获异常,跳转页面
五、shiro的密码加密和加密处理
密码加密处理也是认证的一部分
认证就是账号密码认证的过程
MD5 sha1 内容摘要
能将字符串转化为另一种字符串,过程不可以逆转!
1. shiro提供的的数据操作工具类
1. Base64编码解码
可以将一个字符串或者字节数装华为一个更短的字符串,同样支持翻转
好处:1.存储的数据不是明文 类似加密的手段
2.数据更短!String s = Base64.encodeToString(str.getBytes(“UTF-8”));
System.out.println("s = " + s);
String s1 = Base64.decodeToString(s.getBytes(“UTf-8”));2. MD5、sha1工具类
new Md5Hash(需要转码的字符串, 盐,反复加盐的次数)
new Sha1Hash(转码的字符串, 盐, 反复加盐的次数)
六、shiro 整合商品展示的crud
商品展示
商品添加
权限
一般用户商品展示
管理员有添加功能
商品展示和商品添加都需要登陆
登陆页面任何人都可以直接访问
数据库表
用户表
中间表
角色表
中间表
权限表
商品表
技术:rbac\ssm\layui\freemarker\web\mysql
升级版:shiro
实现步骤:
- 数据库
- 项目搭建()
- ssm整合
七、shiro手动权限验证
手动验证和失败后的处理
需要我们在controller中进行代码判断+spring mvc异常处理!
八、shiro注解的使用
注解形式权限验证优点:
不干扰正常的代码的情况下添加权限管理,而且支持角色权限的or
注意注解里面的方法全部使用的是check,也就是如果正常就可以正常访问,否则抛异常
使用步骤:
步骤一:
在springmvc的配置文件中开启shiro的注解形式
<!-- 使用shiro注解 --> <aop:aspectj-autoproxy proxy-target-class="true" /> <!-- 注解使用声明好的securitymanager--> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager" /> </bean>
步骤二 控制权限
@RequiresAuthentication : 表示当前Subject已经通过login 进行了身份验证;即 Subject. isAuthenticated() 返回 true (了解) authc
@RequiresUser : 表示当前 Subject 已经身份验证或者通过记住我登录的.(了解) user
@RequiresGuest : 表示当前Subject没有身份验证或通过记住我登录过,即是游客身份.(了解) anon
@RequiresRoles : 表示当前 Subject 需要角色. roles
@RequiresPermissions : 表示当前 Subject 需要权限. perms
九、过滤器
如何实现的?
过滤器优点:可以在访问之前进行权限控制,几乎不影响征程的业务逻辑
使用:
1:web.xml 文件中申明一个代理过滤器,代理过滤器帮助我们进行其他过滤器的初始化
<filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <async-supported>true</async-supported> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
2:可以申明对应饿的过滤器连
语法:路径=过滤器[],过滤器
<!-- 创建shiroFilter工厂实例 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- 设置安全管理器 --> <property name="securityManager" ref="securityManager" /> <!-- 设置登录页面 --> <property name="loginUrl" value="/login.jsp" /> <!-- 设置没有权限后跳转的页面 --> <property name="unauthorizedUrl" value="403.jsp" /> <!-- 登录成功后跳转的页面 --> <property name="successUrl" value="success.jsp" /> <!-- 设置过滤器链 --> <property name="filterChainDefinitions"> <value> <!-- 从上向下依次匹配 --> /login.jsp = anon /user/login = anon /logout = logout <!-- 配置权限过滤器 --> /user/roleTest1 = roles[超级管理员,人事] /user/permTest1 = perms[product:delete,product:insert] /* = authc </value> </property> </bean>
3:常见的过滤器
认证相关
authc:认证---------subject.isAuthenticated()
anon
logout
user
授权相关的
roles
perms
十、自定义shiro过滤器
public class OrRolesFilter extends AuthorizationFilter {
public OrRolesFilter() {
}@Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { // 获得主体. Subject subject = this.getSubject(request, response); // 获取配置文件中的数组. String[] rolesArray = (String[])((String[])mappedValue); // 如果数组不为null,并且长度不为0 if (rolesArray != null && rolesArray.length != 0) { // 将数组转换为set集合 Set<String> roles = CollectionUtils.asSet(rolesArray); // 手动判断!!!唯一修改的位置!!! for (String role : roles) { if(subject.hasRole(role)){ return true; } } // 如果一个都不匹配再返回false,拦截. return false; } else { // 数组没值,直接方式. return true; } }
配置自定义配置
<!-- 创建自定义过滤器 --> <bean id="orRoles" class="com.qf.filter.OrRolesFilter" /> <!-- 创建shiroFilter工厂实例 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- 设置安全管理器 --> <property name="securityManager" ref="securityManager" /> <!-- 设置登录页面 --> <property name="loginUrl" value="/login.jsp" /> <!-- 设置没有权限后跳转的页面 --> <property name="unauthorizedUrl" value="403.jsp" /> <!-- 登录成功后跳转的页面 --> <property name="successUrl" value="success.jsp" /> <!-- 添加自定义过滤器 --> <property name="filters"> <map> <entry key="orRoles" value-ref="orRoles"></entry> </map> </property> <!-- 设置过滤器链 --> <property name="filterChainDefinitions"> <value> <!-- 从上向下依次匹配 --> /login.jsp = anon /user/login = anon /logout = logout <!-- 配置权限过滤器 --> /user/roleTest1 = roles[超级管理员,人事] /user/permTest1 = perms[product:delete,product:insert] <!-- 使用自定义过滤器 --> /user/roleTest2 = orRoles[超人,人事] /* = authc </value> </property> </bean>
十一、jsp使用shiro权限标签
不同角色或权限显示不同的页面内容
jsp中引入shiro标签
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> <html> <head> <title>Title</title> </head> <body> 1. 用户名: <shiro:principal /><br /> <shiro:user>2. 用户认证过,或者记住我了.</shiro:user><br /> <shiro:authenticated>3. 用户认证过了就显示我.</shiro:authenticated><br /> <shiro:guest>4. 没认证过,就显示我.</shiro:guest><br /> <shiro:hasRole name="人事">5. 有人事角色就显示我.</shiro:hasRole><br /> <shiro:hasAnyRoles name="超级管理员,超人">6. 有超级管理员或超人角色就显示我.</shiro:hasAnyRoles><br /> <shiro:hasPermission name="product:delete">7. 有product:delete资源权限显示我.</shiro:hasPermission><br /> <shiro:notAuthenticated>8. 没认证过显示我.</shiro:notAuthenticated><br /> <shiro:lacksRole name="超人" >9. 没有超人角色显示我.</shiro:lacksRole><br /> <shiro:lacksPermission name="user:delete" >10. 没有user:delete资源权限显示我.</shiro:lacksPermission><br /> </body> </html>
十二、freemarker使用shiro权限标签
导入依赖: <dependency> <groupId>net.mingsoft</groupId> <artifactId>shiro-freemarker-tags</artifactId> <version>0.1</version> </dependency>
2.修改原有的页面静态化的配置类 public class MyFreemarkerConfigurer extends FreeMarkerConfigurer { @Override public void afterPropertiesSet() throws IOException, TemplateException { super.afterPropertiesSet(); //支持默认标签 Configuration configuration = this.getConfiguration(); //告诉页面静态化支持shiro标签 configuration.setSharedVariable("shiro", new ShiroTags()); } }
配置spring mvc的页面静态化工具
<bean id="configruration" class="com.itqf.configurer.MyFreemarkerConfigurer"> <!-- 模板放置的位置 / classpath resources--> <property name="templateLoaderPath" value="/WEB-INF/ftl/" /> <property name="defaultEncoding" value="UTF-8" /> </bean>
3.ftl文件中使用shiro的标签
<@shiro.guest>游客登录</@shiro.guest> <@shiro.authenticated> <@shiro.principal property="username"/> </@shiro.authenticated> <@shiro.notAuthenticated> 当前身份未认证(包括记住我登录的) </@shiro.notAuthenticated> <@shiro.hasRole name="用户"> 用户[<@shiro.principal/>]用户<br/> </@shiro.hasRole> <@shiro.hasAnyRoles name="用户,管理员,member"> 用户[<@shiro.principal/>]拥有角色用户,管理员<br/> </@shiro.hasAnyRoles> <@shiro.lacksRole name="admin"> 用户[<@shiro.principal/>]不拥有admin角色 </@shiro.lacksRole> <@shiro.hasPermission name="user/add"> 用户[<@shiro.principal/>]拥有user/add权限 </@shiro.hasPermission> <@shiro.lacksPermission name="user/add"> 用户[<@shiro.principal/>]不拥有user/add权限 </@shiro.lacksPermission> <@shiro.user> 记住我/认证显示
十三、记住我
1.设置shiro配置文件添加cookie的设置
2.login之前需要设置rememberme状态
UsernamePasswordToken usernamePasswordToken =
new UsernamePasswordToken(user.getUsername(),user.getPassword());
//记住我
//记住我功能开启!如果认证通过,将返回的账号信息存到cookie recookie
usernamePasswordToken.setRememberMe(true);3.user和authc
user是记住我状态
authc是认证!注意:
要求记住我存储的数据对象全部需要实现序列化接口!否则会出现 deletemecookie!
十四、会话管理
suject.login() 默认是30分钟!
设置认证有效时间:
1.web.xml session-config timeout 单位分钟
2.shiro设计的方法
<!-- 单位是毫秒 超时时间-->
<property name="globalSessionTimeout" value="10000"></property>
<!-- 过期session是否删除-->
<property name="deleteInvalidSessions" value="true"></property>
</bean>