[今日课程大纲]
Shiro简介及架构图讲解
ini配置文件讲解
Shiro搭建及简单认证实现
加密及凭证匹配器
Spring整合Shiro完成登录功能.
[知识点详解]
一.Shiro简介及架构图讲解
1. Shiro官网介绍
1.1 中文:Apache Shiro是一个强大的并且简单使用的java权限框架.主要应用认证(Authentication),授权(Authorization),cryptography(加密),和Session Manager.Shiro具有简单易懂的API,使用Shiro可以快速并且简单的应用到任何应用中,无论是从最小的移动app到最大的企业级web应用都可以使用.
- Shiro几大核心功能.
2.1 Authentication:认证.用户的登录,退出属于认证.
2.2 Authorization:授权.菜单授权,元素可见性授权等.
2.3 Cryptography:加密.密码加密,md5加密等.
2.4 Session Management: Session管理
2.5 Web Integration : Web集成.
2.5.1 Shiro不依赖于容器的,可以运行在JavaSE的环境中.
- Shiro架构图.
3.1 最上面部分表示Shiro支持各种类型的语言.
3.2 Subject:主体.每个用户登录后都会对应一个Subject对象,所有用户信息都存储到Subject中.
3.3 Security Manager:Shiro最大的容器.Shiro所有功能都封装在Security Manager中.
3.3.1 编写代码时,想要使用Shiro第一个步骤获取到Security Manager
3.3.2 在整个程序中保证Security Manager有且只有一个.
3.4 Authenticator: 认证器.执行认证过程调用的组件.
3.5 Authorizer:授权器.执行授权时调用的组件.
3.6 Session Manager: Session管理器.
3.7 Cache Manager: 缓存管理器.Shiro支持很多第三方缓存工具.例如Ehcache等.
3.8 SessionDao: 操作Session内容的组件.
3.9 Realm:该组件的作用负责获取到数据库中数据并根据用户自定义逻辑进行授权和认证等操作.
3.9.1 Shiro 框架和数据库没有关系.没有对数据库设计有认证强制要求.
3.9.2 如果Shiro想要访问数据库都是通过Realm组件.
3.9.3 如果Shiro数据不是来源于数据库,可以使用.ini文件设置静态数据.
- INI文件介绍
- INI应用名称(InitializationFile)
- INI文件是Window系统配置文件的扩展名.
- Shiro的全局配置文件就是.ini文件.
- .ini文件内容的语法和.properties类似都是key=value,value格式.
- INI文件中包含了四个部分
5.1 [main] 主体部分.
5.1.1 这部分配置类对象,或设置属性等操作.
5.1.2 内置了根对象,securityManager
5.1.3 语法:
[main] securityManager.属性=值 key=value securityManager.对象属性=com.bjsxt.pojo.People #后面值是字符串
peo=com.bjsxt.pojo.People securityManager.对象属性=$peo #出现$时才表示是引用对象 |
5.2 [users] 定义用户,密码及用户可以具有的角色.
5.2.1 语法:
[users] 用户名=密码,角色1,角色2 #角色部分可以省略. zhangsan=zs zhangsan=zs,role1,role2 |
5.3 [roles] 定于角色具有的权限
[roles] 角色名=权限名,权限名 role1=user:insert,user:update role2=insert,update role3=user:* |
5.4 [urls] 定义哪个控制器被哪个过滤器过滤.
[urls] 控制器名称=过滤器名称 /login=authc /**=anon |
- Shiro环境搭建及认证实现
- 认证流程
- 获取主体,通过主体Subject对象的login方法进行登录
- 把Subject中内容传递给Security Manager
- Security Manager内部组件Authenticator进行认证,
- 认证数据使用InI Realm,调用Ini文件中数据.
- Principal: 身份.代表用户名,邮箱,手机等能够唯一确认身份的信息.
- Credential: 凭证,代表密码等.
- AuthenticationInfo:认证时存储认证信息
4.1 如果认证是返回值为null,认证认证时账户不存在.
- 搭建步骤:
5.1 在pom.xml中依赖shiro-core
<dependencies> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.3.2</version> </dependency> </dependencies> |
5.2 在java/main/resources创建shiro配置文件shiro.ini
[users] zhangsan=zs lisi=ls |
5.3 编写认证测试代码
5.3.1 SecurityUtils主要作用能够保证Subject在线程容器中.随时取出Subject
public static void main(String[] args) { //创建工厂 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); //创建容器 SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager); //底层使用ThreadLocal所以只要线程不变,可以在任何位置获取到Subject对象. Subject subject = SecurityUtils.getSubject();
//执行登录 AuthenticationToken token = new UsernamePasswordToken("zhangs123an", "zs"); //login方法没有返回值,通过抛出异常告诉用户是什么问题 //org.apache.shiro.authc.UnknownAccountException 帐号不存在 //org.apache.shiro.authc.IncorrectCredentialsException 密码错误 try { subject.login(token); System.out.println("登录成功"); } catch (UnknownAccountException e) { e.printStackTrace(); System.out.println("账户不正确"); }catch(IncorrectCredentialsException e){ System.out.println("密码不正确"); } } |
- 加密及凭证匹配器
- 安全的项目敏感信息存储到数据库中一定不是明文存储.都是对这些敏感信息(例如:密码)进行加密处理.在存储.
- 在Shiro中支持多种加密技术.以MD5加密举例.
- 代码示例
public static void main(String[] args) { String password="zs"; Md5Hash md5Hash = new Md5Hash(password); System.out.println(md5Hash); //加密算法中著名"盐",在程序开发中盐值都是用户的ID等信息. //把原来的字符串和盐拼接,在对拼接的字符串进行加密. Md5Hash md5Hash2 = new Md5Hash(password, "123"); System.out.println(md5Hash2);
Md5Hash md5Hash3 = new Md5Hash(password, "123", 2); System.out.println(md5Hash3);
SimpleHash simpleHash = new SimpleHash("md5",password,"123",2); System.out.println(simpleHash); } |
- SimpleHash 具体的Md5Hash的父类.在SimpleHash中有全局属性
4.1 algorithmName:算法名称
4.2 iterations:迭代几次
- 自定义Realm
- Realm 是Shiro读取数据库(ini)的组件.
- 实现步骤:
2.1 新建类.继承后具备认证和授权功能
public class MyRealm extends AuthorizingRealm |
2.2 doGetAuthenticationInfo(AuthenticationToken token)实现认证功能.
2.2.1 token.getPrincipal() 获取身份
2.2.2 new String((char[])token.getCredentials()) 获取凭证
2.2.3 如果返回值为null,抛出异常帐号不存在
2.2.4 如果返回值AuthenticationInfo第二个参数和login()时密码不一致,抛出异常.第二个参数设置从数据库查询出来的密码
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("执行认证"); //JDBC代码 System.out.println("principal:"+token.getPrincipal()); //先判断用户名是否存在 select * from users where username=? //判断查询结果,如果SQL命令查询结果为null,说明帐号不存在.return null; //如果SQL命令查询结果不为null.必须返回SimpleAuthenticationInfo,第二个参数写从数据库中查询出来的密码 //由Shiro判断第二个参数和token中密码是否匹配. //第三个参数.Map集合的key.当用户登录成功后会把用户的Principal存储到集合中,realmName就是集合key,把用户名当作realmName long id = 123; String username="zhangsan"; String password="5605c3b6f0dc63c2da52625e9d2ed2d2"; AuthenticationInfo info = new SimpleAuthenticationInfo(token.getPrincipal(), password,token.getPrincipal().toString());
System.out.println("Credentials:"+new String((char[])token.getCredentials()));
return info; } |
2.3 在shiro.ini中配置自定义Realm
[main]
myrealm=com.bjsxt.realm.MyRealm securityManager.realms=$myrealm |
- 加密
- 在doGetAuthenticationInfo()中创建AuthenticationInfo对象时,多设置一个参数,参数表示盐
1.1 第三个参数类型ByteSource类型
AuthenticationInfo info = new SimpleAuthenticationInfo(token.getPrincipal(), password,ByteSource.Util.bytes(id+"") ,token.getPrincipal().toString()); |
- 配置shiro.ini凭证匹配器
[main] credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher credentialsMatcher.hashAlgorithmName=md5 credentialsMatcher.hashIterations=2
myrealm=com.bjsxt.realm.MyRealm myrealm.credentialsMatcher=$credentialsMatcher securityManager.realms=$myrealm |
- Spring整合Shiro
- Shiro在web项目中生效都是基于Filter
- DelegatingFilterProxy:把过滤到的请求转发给<bean>
2.1 必须配置在web.xml
2.2 全局(初始化参数<init-param>)targetBeanName,配置过滤的请求交给哪个<bean>处理.属性取值为<bean>的id值
2.3 targetFilterLifecycle默认值为false
- org.apache.shiro.spring.web.ShiroFilterFactoryBean 处理DelegatingFilterProxy 交给的请求.
3.1 securityManager 属性.
3.2 filterChainDefinitions 配置哪些URL进行什么样的权限操作
3.2.1 /login=authc
3.3 loginUrl:表示认证完成后如果认证失败跳转到哪里
3.3.1 内容必须是authc处理的url
3.4 successUrl:表示认证成功后跳转到哪里
- org.apache.shiro.web.mgt.DefaultWebSecurityManager 是SecurityManager接口的实现类
4.1 如果希望自定义Realm,需要给属性realms赋值.
- 配置自定义Realm类的<bean>
5.1 配置属性credentialsMatcher,引用另一个<bean>
- org.apache.shiro.authc.credential.HashedCredentialsMatcher
6.1 配置算法:hashAlgorithmName
6.2 配置迭代次数:hashIterations
- FormAuthenticationFilter(authc)
7.1 如果认证失败把异常信息放入到request作用域中,并设置key为下面
7.2 如果希望FormAuthenticationFilter生效,表单中用户名必须叫做username,密码必须叫做password
- 简易流程图
|
|
|
|
结束 |
|
|
开始 |
请求/login控制器 |
DelegatingFilterProxy过滤 |
securityManager,让shiro生效 |
- 整合步骤
- 在bjsxt-parent和bjsxt-manage的pom.xml导入3个jar
<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> |
- 在bjsxt-manage中web.xml配置过滤器
<!-- shiro --> <filter> <filter-name>shiro</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetBeanName</param-name> <param-value>shiroFilter</param-value> </init-param> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>shiro</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> |
- 新建类com.bjsxt.manage.realm.MyShiroRealm
public class MyShiroRealm extends AuthorizingRealm{ @Autowired private UsersService usersService;
@Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { // TODO Auto-generated method stub return null; } //认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("usersService:"+usersService); System.out.println("zhixing认证"); String principal = token.getPrincipal().toString(); System.out.println("principal:"+principal); Users user = usersService.login123(principal); System.out.println("dubbo调用陈功"); if(user!=null){ System.out.println("if"); AuthenticationInfo info= new SimpleAuthenticationInfo(token.getPrincipal(), user.getPassword(),ByteSource.Util.bytes(user.getId()+""),token.getPrincipal().toString()); return info; }else{ System.out.println("else"); //表示用户名不存在 return null; } } } |
- 在src/main/resources下新建applicationContext-shiro.xml
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <property name="hashAlgorithmName" value="md5"></property> <property name="hashIterations" value="2"></property> </bean> <bean id="myShiroRealm" class="com.bjsxt.manage.realm.MyShiroRealm"> <property name="credentialsMatcher" ref="credentialsMatcher"></property> </bean> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realms" ref="myShiroRealm"></property> </bean> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"></property> <property name="loginUrl" value="/login"></property> <property name="successUrl" value="/loginSuccess"></property> <property name="filterChainDefinitions"> <value> /login=authc /**=anon </value> </property> </bean> |
- 其中DUbbo代码和bjsxt-manage的service代码省略
- 在bjsxt-manage的控制器中
@RequestMapping("login") @ResponseBody public int login(HttpServletRequest request){ Object obj = request.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME); System.out.println("obj:"+obj); if(obj!=null){ if(obj.equals(UnknownAccountException.class.getName())){ //账户不正确. return 1; }else if(obj.equals(IncorrectCredentialsException.class.getName())){ //密码不正确. return 2; } } return 3; } /** * 成功 * @return */ @RequestMapping("loginSuccess") @ResponseBody public int loginSuccess(){ System.out.println("执行success"); return 4; } |
- 修改Dubbo的@Referece注解为配置文件方式
7.1 在applicationCOntext-dubbo.xml中修改如下
<!-- 注解扫描 --> <!-- <dubbo:annotation package="com.bjsxt.manage.service.impl"/> --> <dubbo:reference id="usersDubboService" interface="com.bjsxt.dubbo.service.UsersDubboService" ></dubbo:reference> <context:component-scan base-package="com.bjsxt.manage.service.impl"></context:component-scan> |
7.2 在bjsxt-manage的Service实现类修改如下
@Autowired // @Reference private UsersDubboService usersDubboService; |