shiro安全框架
1.1.Shiro 概述
Shiro是apache旗下一个开源安全框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,权限授权、加密、会话管理等功能,组成了一个通用的安全认证框架,使用shiro就可以非常快速的完成认证、授权等功能的开发,降低系统成本。
课后了解:Spring security 安全框架
1.2.Shiro 概要架构
在概念层,Shiro 架构包含三个主要的理念:Subject,SecurityManager和 Realm。
1.3shiro的构件
1)Subject(主体):与软件交互的一个特定的实体(用户、第三方服务等)。
2)SecurityManager(安全管理器) :Shiro 的核心,用来协调管理组件工作。
3)Authenticator(认证管理器):负责执行认证操作
4)Authorizer(授权管理器):负责授权检测
5)SessionManager(会话管理):负责创建并管理用户 Session 生命周期,提供一个强有力的 Session 体验。
6)SessionDAO:代表 SessionManager 执行 Session 持久(CRUD)动作,它允许任何存储的数据挂接到 session 管理基础上。
7)CacheManager(缓存管理器):提供创建缓存实例和管理缓存生命周期的功能
8)Cryptography(加密管理器):提供了加密方式的设计及管理。
9)Realms(领域对象):是shiro和你的应用程序安全数据之间的桥梁。
2.1Shiro 认证
简述:
1)系统调用subject的login方法将用户信息提交给SecurityManager
2)SecurityManager将认证操作委托给认证器对象Authenticator
3)Authenticator将身份信息传递给Realm。
4)Realm访问数据库获取用户信息然后对信息进行封装并返回。
5)Authenticator 对realm返回的信息进行身份认证。
详述:
以用户名密码验证为例,当浏览器客户端向服务器发送AJAX异步请求,请求服务器处理验证用户输入的用户名和密码,
当请求传入Tomcat,通过Tomcat的过滤器****到达前端控制器的时候,前端控制器截取请求的url,在HandlerMapping里找对应的map集合中的entry元素,找到后穿过springMVC的拦截器到达后端控制器Controller,在Controller里使用SecorityUtils获取一个Subject,subject里面有一个login方法,但是调用login方法必须传入一个token类型的参数,这时候我们想到AJAX里面传过来的username和password正好可以封装在一个UsernamePasswordtoken里面,所以我们这样写
UsernamePasswordToken tokensubjec = new UsernamePasswordToken(username,password);
Subject subject = SecurityUtils.Subject();
subject.login(token);
当我们写完这句subject.login(token);
系统底层会帮我们从配置文件读取我们配置的SecurityManager以及相关配置,并将token传过去,会找到anthenticator(认证管理器),anthenticator根据SecurityManager里面配置的realm,找到realm。我们要做的就是在这之前一定要确保该realm已经存在,否则会找不到,在realm中我们将传过来token进行向下造型回usernamepasswordtoken,
然后将里面的username取出来,传递到数据层,让数据层根据username从用户名中查询具体用户所有信息,查询完毕我们使用一个SimpleAuthenticationInfo进行信息封装,new一个该对象的时候我们要传入四个参数,第一个是身份信息,我们一般传入从dao数据层查到的用户信息,但是要注意这里写入的类型和授权的时候取数据的类型要对应,如果只写用户名则取数据的时候用字符串接收,第二个参数是加密后的密码,我们已经查到,第三个参数是指定类型的salt,其实和我们通过数据层查到的salt是一样的只不过需要的类型不一致,需要做一步转型即可,第四个参数是realm的名字,表示本类直接用this.getName();然后将我们new出来的SimpleAuthenticationInfo对象的实例返回给认证管理器,认证管理器会自动帮我们认证。
如下图所示
2.2Shiro的授权
概述:
1)系统调用subject相关方法将用户信息(例如isPermitted)递交给SecurityManager
2)SecurityManager将权限检测操作委托给Authorizer对象
3)Authorizer将用户信息委托给realm.
4)Realm访问数据库获取用户权限信息并封装。
5) Authorizer对用户授权信息进行判定。
3.基于注解的方式配置Shiro组件
@Bean("securityManager")
public DefaultWebSecurityManager newDefaultWebSecurityManager(
AuthorizingRealm userRealm){
DefaultWebSecurityManager sManager=
new DefaultWebSecurityManager();
//此时必须保证realm对象已经存在了
sManager.setRealm(userRealm);
return sManager;
}
@Bean("shiroFilterFactoryBean")
public ShiroFilterFactoryBean newShiroFilterFactoryBean(
SecurityManager securityManager){//shiro 包
ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager);
//当此用户是一个非认证用户,需要先登陆进行认证
bean.setLoginUrl("/doLoginUI.do");
LinkedHashMap<String,String> fcMap=
new LinkedHashMap<>();
fcMap.put("/bower_components/**","anon");//anon表示允许匿名访问
fcMap.put("/build/**", "anon");
fcMap.put("/dist/**","anon");
fcMap.put("/plugins/**","anon");
fcMap.put("/doLogin.do","anon");
fcMap.put("/doLogout.do","logout");
fcMap.put("/**", "authc");//必须授权才能访问
bean.setFilterChainDefinitionMap(fcMap);
return bean;
}
@Bean("lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor newLifecycleBeanPostProcessor(){
return new LifecycleBeanPostProcessor();
}
@DependsOn(value="lifecycleBeanPostProcessor")
@Bean
public DefaultAdvisorAutoProxyCreator newDefaultAdvisorAutoProxyCreator(){
return new DefaultAdvisorAutoProxyCreator();
}
@Bean
public AuthorizationAttributeSourceAdvisor newAuthorizationAttributeSourceAdvisor(
SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor bean=
new AuthorizationAttributeSourceAdvisor();
bean.setSecurityManager(securityManager);
return bean;
}
3.3.Shiro 核心过滤器配置
在注解启动类中,重写onStartup方法,完成过滤器的注册
@Override
public void onStartup(ServletContext servletContext)
throws ServletException {
System.out.println("onStartup");
//super.onStartup(servletContext);
registerContextLoaderListener(servletContext);
registerFilter(servletContext);
registerDispatcherServlet(servletContext);
}
private void registerFilter(ServletContext servletContext) {
//注册Filter对象
//项目没有web.xml并且此filter不是自己写的,所以需要用这种配置方式
FilterRegistration.Dynamic dy=
servletContext.addFilter("filterProxy",
DelegatingFilterProxy.class);
dy.setInitParameter("targetBeanName","shiroFilterFactoryBean");
dy.addMappingForUrlPatterns(
null,//EnumSet<DispatcherType>
false,"/*");//url-pattern
}
4.基于xml方式配置Shiro组件
<?xml version="1.0" encoding="UTF-8"?>
<beans default-lazy-init="true"
xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:util="http://www.springframework.org/schema/util"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.3.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 配置securityManager -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- 配置Realm -->
<property name="Realms" ref="shiroUserRealm"></property>
</bean>
<!-- 生命周期管理器 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor">
</bean>
<!--设置自动代理Service实现类-->
<bean id="defaultAdvisorAutoProxyCreator"
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor">
</bean>
<!-- 配置授权属性 -->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor" >
<property name="SecurityManager" ref="securityManager"></property>
</bean>
<!-- 配置shiro的shiroFilterFactoryBean -->
<!-- 目的:让用户先进行登录认证 -->
<bean id="shiroFilterFactory" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="SecurityManager" ref="securityManager"></property>
<property name="LoginUrl" value="/doLoginUI.do"></property>
<!-- 设置请求过滤规则 -->
<property name="FilterChainDefinitionMap">
<map>
<entry key="/bower_components/**" value="anon"></entry>
<entry key="/build/**" value="anon"></entry>
<entry key="/dist/**" value="anon"></entry>
<entry key="/plugins/**" value="anon"></entry>
<entry key="/user/doLogin.do" value="anon"></entry>
<entry key="/doLogout.do" value="logout"></entry>
<!-- 除了以上的资源,其他资源都需要登录认证 -->
<entry key="/**" value="authc"></entry>
</map>
</property>
</bean>
</beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<display-name>CGB-JT-SYS-V1.01</display-name>
<!-- Shiro中的核心过滤器(负责拦截请求) -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>shiroFilterFactory</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置Spring MVC 前端控制器 -->
<!-- 注册前端控制器 -->
<servlet>
<servlet-name>frontController</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name><!-- 这个名字不能随意写 -->
<param-value>classpath:spring-configs.xml</param-value>
</init-param>
<!--配置servlet在服务器启动时加载(数字越小优先级越高)
配置如下选项以后,tomcat启动时就会初始化这个servlet,
这个servlet在初始化时会读取contextConfigLocation
参数对应的配置文件.
-->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 配置前端控制器映射 -->
<servlet-mapping>
<servlet-name>frontController</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>