1.shiro认证

1.1.身份验证

身份验证:一般需要提供如身份ID等一些标识信息来表明登录者的身份,如提供email,用户名/密码来证明。

在shiro中,用户需要提供principals(身份)和credentials(证明)给shiro,从而应用能验证用户身份。

principals:身份,即主体的标识属性,可以是任何属性,如用户名、邮箱等,唯一即可。一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/邮箱/手机号。

credentials:证明/凭证,即只有主体知道的安全值,如密码/数字证书等。

最常见的principals和credentials组合就是用户名/密码。

1.2.shiro认证思路分析

(1)获取当前的Subject,调用SecurityUtils.getSubject();

(2)测试当前的用户是否已经被认证,即是否已经登录。调用Subject的isAuthenticated();

(3)若没有被认证,则把用户名和密码封装为UsernamePasswordToken对象。

①创建一个表单页面

②把请求提交到SpringMVC的Handler

③获取用户名和密码

(4)执行登录:调用Subject的login(AuthenticationToken)方法。

(5)自定义Realm的方法,从数据库中获取对应的记录,返回给Shiro。

①实际上需要继承org.apache.shiro.realm.AuthenticatingRealm类

②实现doGetAuthenticationInfo(AuthenticationToken)方法

(6)由shiro完成对密码的比对。

1.3.实现认证的相应代码

这部分包含了MD5加密、MD5盐值加密、多realms、认证策略

(1)编写相应的login.jsp、list.jsp页面。

(2)controller

@Controller
@RequestMapping("/shiro")
public class ShiroController {@RequestMapping("/login")public String login(@RequestParam("username") String username, @RequestParam("password") String password){Subject currentUser = SecurityUtils.getSubject();if (!currentUser.isAuthenticated()) {// 把用户名和密码封装为 UsernamePasswordToken对象UsernamePasswordToken token = new UsernamePasswordToken(username, password);token.setRememberMe(true);try {currentUser.login(token);}catch (AuthenticationException ae) {System.out.println("登录失败: " + ae.getMessage());}}return "redirect:/list.jsp";}
}

(3)第一个realm,ShiroRealm.java,密码采用MD5盐值加密。盐值需要唯一:一般使用随机字符串或 user id。

public class ShiroRealm extends AuthenticatingRealm {@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {System.out.println("[FirstRealm] doGetAuthenticationInfo");//1. 把 AuthenticationToken 转换为 UsernamePasswordTokenUsernamePasswordToken upToken = (UsernamePasswordToken) token;//2. 从 UsernamePasswordToken 中来获取 usernameString username = upToken.getUsername();//3. 调用数据库的方法, 从数据库中查询 username 对应的用户记录System.out.println("从数据库中获取 username: " + username + " 所对应的用户信息.");//4. 若用户不存在, 则可以抛出 UnknownAccountException 异常if("unknown".equals(username)){throw new UnknownAccountException("用户不存在!");}//5. 根据用户信息的情况, 决定是否需要抛出其他的 AuthenticationException 异常.if("monster".equals(username)){throw new LockedAccountException("用户被锁定");}//6. 根据用户的情况, 来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为: SimpleAuthenticationInfo//以下信息是从数据库中获取的.//1). principal: 认证的实体信息. 可以是 username, 也可以是数据表对应的用户的实体类对象.Object principal = username;//2). credentials: 密码.Object credentials = null; //"fc1709d0a95a6be30bc5926fdb7f22f4";if("admin".equals(username)){credentials = "038bdaf98f2037b31f1e75b5b4c9b26e";}else if("user".equals(username)){credentials = "098d2c478e9c11555ce2823231e02ec1";}//3). realmName: 当前 realm 对象的 name. 调用父类的 getName() 方法即可String realmName = getName();//4). 盐值.ByteSource credentialsSalt = ByteSource.Util.bytes(username);SimpleAuthenticationInfo info = null; //new SimpleAuthenticationInfo(principal, credentials, realmName);info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);return info;}public static void main(String[] args) {String hashAlgorithmName = "MD5";Object credentials = "123456";Object salt = ByteSource.Util.bytes("user");;int hashIterations = 1024;Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);System.out.println(result);}
}

第二个realm。SecondRealm.java,密码采用SHA1盐值加密。

public class SecondRealm extends AuthenticatingRealm {@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {System.out.println("[SecondReaml] doGetAuthenticationInfo");//1. 把 AuthenticationToken 转换为 UsernamePasswordTokenUsernamePasswordToken upToken = (UsernamePasswordToken) token;//2. 从 UsernamePasswordToken 中来获取 usernameString username = upToken.getUsername();//3. 调用数据库的方法, 从数据库中查询 username 对应的用户记录System.out.println("从数据库中获取 username: " + username + " 所对应的用户信息.");//4. 若用户不存在, 则可以抛出 UnknownAccountException 异常if("unknown".equals(username)){throw new UnknownAccountException("用户不存在!");}//5. 根据用户信息的情况, 决定是否需要抛出其他的 AuthenticationException 异常.if("monster".equals(username)){throw new LockedAccountException("用户被锁定");}//6. 根据用户的情况, 来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为: SimpleAuthenticationInfo//以下信息是从数据库中获取的.//1). principal: 认证的实体信息. 可以是 username, 也可以是数据表对应的用户的实体类对象.Object principal = username;//2). credentials: 密码.Object credentials = null; //"fc1709d0a95a6be30bc5926fdb7f22f4";if("admin".equals(username)){credentials = "ce2f6417c7e1d32c1d81a797ee0b499f87c5de06";}else if("user".equals(username)){credentials = "073d4c3ae812935f23cb3f2a71943f49e082a718";}//3). realmName: 当前 realm 对象的 name. 调用父类的 getName() 方法即可String realmName = getName();//4). 盐值.ByteSource credentialsSalt = ByteSource.Util.bytes(username);SimpleAuthenticationInfo info = null; //new SimpleAuthenticationInfo(principal, credentials, realmName);info = new SimpleAuthenticationInfo("secondRealmName", credentials, credentialsSalt, realmName);return info;}public static void main(String[] args) {String hashAlgorithmName = "SHA1";Object credentials = "123456";Object salt = ByteSource.Util.bytes("admin");;int hashIterations = 1024;Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);System.out.println(result);}
}

(4)shiro的相应配置。

注意多relam的配置和认证策略。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><!--1. 配置 SecurityManager!--><bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"><property name="cacheManager" ref="cacheManager"/><property name="authenticator" ref="authenticator"/><property name="realms"><list><ref bean="jdbcRealm"/><ref bean="secondRealm"/></list></property></bean><!-- 2. 配置 CacheManager.    需要加入 ehcache的jar包及配置文件.--><bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"><property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/></bean><!--3. 配置 Realm3.1 直接配置实现了 org.apache.shiro.realm.Realm 接口的 bean--><bean id="jdbcRealm" class="com.lmc.shiro.realms.ShiroRealm"><property name="credentialsMatcher"><bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"><property name="hashAlgorithmName" value="MD5"></property><property name="hashIterations" value="1024"></property></bean></property></bean><bean id="secondRealm" class="com.lmc.shiro.realms.SecondRealm"><property name="credentialsMatcher"><bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"><property name="hashAlgorithmName" value="SHA1"></property><property name="hashIterations" value="1024"></property></bean></property></bean><bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator"><property name="authenticationStrategy"><bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"></bean></property></bean><!--4.配置 LifecycleBeanPostProcessor.可以自动的来调用配置在 Spring IOC容器中 shiro bean 的生命周期方法.--><bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/><!--5.启用IOC容器中使用shiro的注解. 但必须在配置了LifecycleBeanPostProcessor之后才可以使用.--><bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/><bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"><property name="securityManager" ref="securityManager"/></bean><!--6. 配置 ShiroFilter.6.1 id必须和 web.xml 文件中配置的 DelegatingFilterProxy 的 <filter-name> 一致.若不一致, 则会抛出: NoSuchBeanDefinitionException. 因为 Shiro 会来 IOC 容器中查找和 <filter-name>名字对应的 filter bean.--><bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"><property name="securityManager" ref="securityManager"/><property name="loginUrl" value="/login.jsp"/><property name="successUrl" value="/list.jsp"/><property name="unauthorizedUrl" value="/unauthorized.jsp"/><!--配置哪些页面需要受保护.以及访问这些页面需要的权限.1). anon 可以被匿名访问2). authc 必须认证(即登录)后才可能访问的页面.3). logout 登出.4). roles 角色过滤器--><property name="filterChainDefinitions"><value>/login.jsp = anon/shiro/login = anon/shiro/logout = logout<!--/user.jsp = roles[user]/admin.jsp = roles[admin]-->/** = authc</value></property></bean>
</beans>

结果展示:

认证策略(AuthenticationStrategy):如果多个realm,怎样才能认证成功。

AuthenticationStrategy接口的默认实现:

•FirstSuccessfulStrategy:只要有一个Realm验证成功即可,只返回第一个Realm身份验证成功的认证信息,其他的忽略;

•AtLeastOneSuccessfulStrategy:只要有一个Realm验证成功即可,和FirstSuccessfulStrategy不同,将返回所有Realm身份验证成功的认证信息;

•AllSuccessfulStrategy:所有Realm验证成功才算成功,且返回所有Realm身份验证成功的认证信息,如果有一个失败就失败了。

ModularRealmAuthenticator默认是AtLeastOneSuccessfulStrategy策略。

2.shiro授权

授权,也叫访问控制,即在应用中控制谁访问哪些资源(如访问页面/编辑数据/页面操作等)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)。

主体(Subject):访问应用的用户,在Shiro中使用Subject代表该用户。用户只有授权后才允许访问相应的资源。

资源(Resource):在应用中用户可以访问的URL,比如访问JSP页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。

权限(Permission):安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如:访问用户列表页面查看/新增/修改/删除用户数据(即很多时候都是CRUD式权限控制)等。权限代表了用户有没有操作某个资源的权利,即反映在某个资源上的操作允不允许。Shiro支持粗粒度权限(如用户模块的所有权限)和细粒度权限(操作某个用户的权限,即实例级别的)。

角色(Role):权限的集合,一般情况下会赋予用户角色而不是权限,即这样用户可以拥有一组权限,赋予权限时比较方便。典型的如:项目经理、技术总监、CTO、开发工程师等都是角色,不同的角色拥有一组不同的权限。

2.1.shiro授权方式

Shiro支持三种方式的授权:

编程式:通过写if/else授权代码块完成。

注解式:通过在执行的Java方法上放置相应的注解完成,没有权限将抛出相应的异常。

JSP/GSP标签:在JSP/GSP页面通过相应的标签完成。

2.2.授权的实现

授权需要继承AuthorizingRealm类,并实现其doGetAuthorizationInfo方法。

AuthorizingRealm类继承自AuthenticatingRealm,但没有实现AuthenticatingRealm中的doGetAuthenticationInfo,所以认证和授权只需要继承AuthorizingRealm就可以了,同时实现他的两个抽象方法。

(1)编写相应的页面list.jsp和群演页面admin.jsp、user.jsp、unauthorized.jsp。

list.jsp

<%@ 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>
<h4>List Page</h4>Welcome: <shiro:principal></shiro:principal><shiro:hasRole name="admin"><br><br><a href="admin.jsp">Admin Page</a>
</shiro:hasRole><shiro:hasRole name="user"><br><br><a href="user.jsp">User Page</a>
</shiro:hasRole><br><br>
<a href="shiro/logout">Logout</a>
</body>
</html>

(2)修改ShiroRealm.java为

public class ShiroRealm extends AuthorizingRealm {@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {System.out.println("[FirstRealm] doGetAuthenticationInfo");//1. 把 AuthenticationToken 转换为 UsernamePasswordTokenUsernamePasswordToken upToken = (UsernamePasswordToken) token;//2. 从 UsernamePasswordToken 中来获取 usernameString username = upToken.getUsername();//3. 调用数据库的方法, 从数据库中查询 username 对应的用户记录System.out.println("从数据库中获取 username: " + username + " 所对应的用户信息.");//4. 若用户不存在, 则可以抛出 UnknownAccountException 异常if("unknown".equals(username)){throw new UnknownAccountException("用户不存在!");}//5. 根据用户信息的情况, 决定是否需要抛出其他的 AuthenticationException 异常.if("monster".equals(username)){throw new LockedAccountException("用户被锁定");}//6. 根据用户的情况, 来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为: SimpleAuthenticationInfo//以下信息是从数据库中获取的.//1). principal: 认证的实体信息. 可以是 username, 也可以是数据表对应的用户的实体类对象.Object principal = username;//2). credentials: 密码.Object credentials = null; //"fc1709d0a95a6be30bc5926fdb7f22f4";if("admin".equals(username)){credentials = "038bdaf98f2037b31f1e75b5b4c9b26e";}else if("user".equals(username)){credentials = "098d2c478e9c11555ce2823231e02ec1";}//3). realmName: 当前 realm 对象的 name. 调用父类的 getName() 方法即可String realmName = getName();//4). 盐值.ByteSource credentialsSalt = ByteSource.Util.bytes(username);SimpleAuthenticationInfo info = null; //new SimpleAuthenticationInfo(principal, credentials, realmName);info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);return info;}public static void main(String[] args) {String hashAlgorithmName = "MD5";Object credentials = "123456";Object salt = ByteSource.Util.bytes("user");;int hashIterations = 1024;Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);System.out.println(result);}//授权会被 shiro 回调的方法@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {//1. 从 PrincipalCollection 中来获取登录用户的信息Object principal = principals.getPrimaryPrincipal();//2. 利用登录的用户的信息来用户当前用户的角色或权限(可能需要查询数据库)Set<String> roles = new HashSet<>();roles.add("user");if("admin".equals(principal)){roles.add("admin");}//3. 创建 SimpleAuthorizationInfo, 并设置其 reles 属性.SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);//4. 返回 SimpleAuthorizationInfo 对象.return info;}
}

(3)shiro配置所做的修改:

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"><property name="securityManager" ref="securityManager"/><property name="loginUrl" value="/login.jsp"/><property name="successUrl" value="/list.jsp"/><property name="unauthorizedUrl" value="/unauthorized.jsp"/><!--配置哪些页面需要受保护.以及访问这些页面需要的权限.1). anon 可以被匿名访问2). authc 必须认证(即登录)后才可能访问的页面.3). logout 登出.4). roles 角色过滤器--><property name="filterChainDefinitions"><value>/login.jsp = anon/shiro/login = anon/shiro/logout = logout/user.jsp = roles[user]/admin.jsp = roles[admin]# everything else requires authentication:/** = authc</value></property></bean>

结果展示:当用户为admin时和当页面为user时页面的展示是不同的。

优化:对于我们的shirofilter,在实际开发中,我们一般都是这样的从数据库表中初始化资源和权限,所以我们采用以下的方式。

public class FilterChainDefinitionMapBuilder {public LinkedHashMap<String, String> buildFilterChainDefinitionMap(){LinkedHashMap<String, String> map = new LinkedHashMap<>();map.put("/login.jsp", "anon");map.put("/shiro/login", "anon");map.put("/shiro/logout", "logout");map.put("/user.jsp", "authc,roles[user]");map.put("/admin.jsp", "authc,roles[admin]");map.put("/list.jsp", "user");map.put("/**", "authc");return map;}}
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"><property name="securityManager" ref="securityManager"/><property name="loginUrl" value="/login.jsp"/><property name="successUrl" value="/list.jsp"/><property name="unauthorizedUrl" value="/unauthorized.jsp"/><property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"/></bean><!-- 配置一个 bean, 该 bean 实际上是一个 Map. 通过实例工厂方法的方式 --><bean id="filterChainDefinitionMap"factory-bean="filterChainDefinitionMapBuilder" factory-method="buildFilterChainDefinitionMap"></bean><bean id="filterChainDefinitionMapBuilder"class="com.lmc.shiro.factory.FilterChainDefinitionMapBuilder"></bean>

对于shiro标签有:guest标签、user标签、authenticated标签、notAuthenticated标签、pincipal标签、hasRole标签、hasAnyRoles标签。

参考:尚硅谷

shiro认证与授权相关推荐

  1. Shiro认证和授权

    shiro介绍 什么是shiro Shiro是apache旗下一个开源框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,权限授权.加密.会话管理等功能,组成了一个通用的安全认证框架. ...

  2. Shiro认证和授权的思路

    认证 (1)获取当前的Subject,调用SecurityUtils.getSubject(): (2)测试当前的用户是否已经被认证,即是否已经登录.调用Subject的isAuthenticated ...

  3. shiro认证与授权:自定义realm

    [main] #声明realm permReam=cn.learn.shiro.PermissionRealm #注册realm到securityManager中 securityManager.re ...

  4. shiro认证与授权:基于ini的用户授权

    [users] #用户名=密码,角色名 zhangsan=123456,role1,role2 lisi=123456,role2 [roles] #角色 #角色名=权限列表 role1=user:s ...

  5. 源码分析shiro认证授权流程

    1. shiro介绍 Apache Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能: 认证 - 用户身份识别,常被称为用户"登录": 授权 - ...

  6. Springboot整合shiro基于url身份认证和授权认证

    你还不会shiro吗? 前奏 shiro核心配置文件(rolesFilter可选). 身份认证 多表登录源如何操作? 授权管理 如何解决界面多角色/资源问题 访问效果 权限管理在日常开发中很重要,所以 ...

  7. authc过滤器 shiro_使用Shiro实现认证和授权(基于SpringBoot)

    Apache Shiro是一个功能强大且易于使用的Java安全框架,它为开发人员提供了一种直观,全面的身份验证,授权,加密和会话管理解决方案.下面是在SpringBoot中使用Shiro进行认证和授权 ...

  8. Shiro+springboot+mybatis+EhCache(md5+salt+散列)认证与授权-03

    从上文:Shiro+springboot+mybatis(md5+salt+散列)认证与授权-02 当每次进行刷新时,都会从数据库重新查询数据进行授权操作,这样无疑给数据库造成很大的压力,所以需要引入 ...

  9. Shiro+springboot+mybatis(md5+salt+散列)认证与授权-02

    代码延续地址:Shiro+springboot+mybatis(md5+salt+散列)认证与授权-01 1.创建t_role角色表(比如管理员admin,普通用户user等),创建t_pers权限表 ...

最新文章

  1. 禁止java更新_禁止:禁止对'replicas','template'和'updateStrategy'以外的字段的statefulset规范进行更新...
  2. 记录一次cefsharp1输入法在win7下异常解决定位
  3. 查看防火墙状态_干货 | 华为防火墙配置,这篇文章强烈推荐收藏学习
  4. 关于网页的一些小知识点
  5. 旋转函数_【视频课】:一次函数拓展应用(图象的平移、旋转、轴对称及5种解题方法)...
  6. Spark的枚举类型实例!scala的枚举。
  7. 11,EasyNetQ-调度事件与定时发布
  8. red hat linux综合实验报告,实验一 Red Hat Linux 9.doc
  9. redis学习-摘抄
  10. 作业车间调度问题特征与调度效率相关性的研究Correlation of job-shop scheduling problem features with scheduling efficiency
  11. ckdeitor的使用方法
  12. Day 6 函数与模块
  13. CMM3学习笔记--常用专业术语
  14. Topic 9. SCI 文章第二张表—单因素回归分析表
  15. 学习日记day25 平面设计 综合例子
  16. 笛卡尔心形线 matlab,笛卡尔心形线
  17. 计算机专业裁合词英语,计算机专业英语的构词方法
  18. 计算机青岛科技大学济南大学,山东考生在山东理工,济大,山东科技和青岛科技中该如何选择?...
  19. 一个传奇玩家的传奇故事
  20. [Winows 软件推荐] 四款常用精品的软件

热门文章

  1. HITRAN数据库的使用以及普朗克平均吸收系数的计算
  2. hdfs上传文件没有权限写入的问题解决put: Permission denied: user=root, access=WRITE
  3. java 模拟简单打印机功能_java 单例模式模拟打印机打印任务
  4. cannot create network xxx(br-xxx) conflicts with network yyy(br-yyy) networks have overlapping IPv4
  5. 如何将手机里的wav录音转换成mp3格式?
  6. Unity C# 中国天气网城市代码 获取当前天气、各种指数方法
  7. IDEA 设置默认注释模板
  8. mysql_num_rows()的作用!
  9. 抢跑分布式存储赛道,精准助力行业转型,这家厂商凭什么?
  10. mu-mimo/ mimo 的解释