spring 集成 shiro安全框架
基于xml 配置文件集成
1. web.xml 文件
<!-- 配置Shiro安全过滤器 --> <filter> <filter-name>shiroFilter</filter-name> <!-- 此类由spring-web 提供 --> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <!-- 这个参数名在DelegatingFilterProxy中定义 --> <param-name>targetBeanName</param-name> <!-- 这个值在spring-shiro.xml配置文件中定义 --> <param-value>shiroFilterFactory</param-value> </init-param> </filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
shiro 的配置文件
spring-shiro.xml
<!-- 配置SecurityManager对象,(Shiro框架核心,负责调用相关组件,实现 用户身份认证,缓存,会话管理等功能 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="Realm" ref="shiroUserRealm"></property> </bean> <!-- 配置ShiroFilterFactoryBean对象 (Shiro中会通过很多过滤器对WEB请求做预处理 ,这些过滤器的创建 底层设计了一个工厂类--> <bean id="shiroFilterFactory" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- 注入securityManager对象 --> <property name="SecurityManager" ref="securityManager"></property> <!-- 配置登录页面 --> <property name="LoginUrl" value="/doLoginUI.do"></property> <!-- 过滤给则(哪些资源允许匿名访问,哪些资源需要授权访问) anon:允许匿名访问 authc需要授权访问 --> <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="/doLogin.do" value="anon"></entry>
<entry key="/doLogout.do" value="logout"></entry> //登出操作 <entry key="/**" value="authc"></entry><!-- 必须认证 --> </map> </property> </bean>
Realm:
package com.demo.realm; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.LockedAccountException; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authc.credential.CredentialsMatcher; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import com.demo.entity.SysUser; import com.demo.mapper.SysUserDao; /** * 借助此对象访问用户身份信息,权限信息并对 * 其进行封装,返回给调用者. * 思考? * 1)此对象由谁来调用?(认证管理器,授权管理器) * @author adminitartor */ @Service public class ShiroUserRealm extends AuthorizingRealm { @Autowired private SysUserDao sysUserDao; //ConcurrentHashMap()在jdk1.5之后推出,在1.8和1.8之后 //添加了红黑树 private Map<String, SimpleAuthorizationInfo> authMap= new ConcurrentHashMap<String, SimpleAuthorizationInfo>(); /*** * 完成授权信息的获取以及封装. * 此方法何时调用?(执行授权检测时调用) * */ @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { System.out.println("realm.doGetAuthorizationInfo"); //1.获取登陆用户身份信息,(基于登录认证时传递的主身份) String username= (String)principals.getPrimaryPrincipal(); //查询缓存中是否有这个 if(authMap.containsKey(username)) return authMap.get(username); //2.查找用户的权限信息 List<String> list=//sys:user:update,sys:user:view,...., sysUserDao.findUserPermissions(username); System.out.println("list="+list); //去重去空 Set<String> permissions=new HashSet<String>(); for(String permission:list){ if(!StringUtils.isEmpty(permission)){ permissions.add(permission); } } System.out.println("====查询认证信息"); //3.对权限信息进行封装 SimpleAuthorizationInfo info= new SimpleAuthorizationInfo(); info.setStringPermissions(permissions); authMap.put(username, info); return info; } /** * 完成认证信息的获取以及封装 * 此方法何时调用?(执行登陆认证时调用) * @param * 用于接收用户身份以及凭证信息的对象(用户输入的) * * @return AuthenticationInfo * 封装了认证信息的对象(从数据库查询到的) * * client-->controller-->service-->realm */ @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { System.out.println("进行身份认证"); //1.获取用户身份信息 UsernamePasswordToken uToken= (UsernamePasswordToken)token; String username=uToken.getUsername(); //String username=token.getPrintcipal(); //2.基于用户身份查询数据库信息 SysUser sysUser= sysUserDao.findUserByUserName(username); if(sysUser==null) throw new UnknownAccountException("密码或账号错误"); if(sysUser.getValid()==0){ throw new LockedAccountException("用户被锁定"); } //3.对查询结果进行封装. //3.1获取用户salt值,并将其转换为一个字节源对象 ByteSource byteSource= ByteSource.Util.bytes(sysUser.getSalt()); //3.2对用户信息进行封装返回. AuthenticationInfo info= new SimpleAuthenticationInfo( sysUser.getUsername(), //主身份 sysUser.getPassword(), //已加密的密码 byteSource,//salt对应的字节源对象 getName());//realm 的名字 return info; } /** * 设置凭证(密码)匹配器 */ @Override public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) { HashedCredentialsMatcher hMatcher = new HashedCredentialsMatcher(); //设置加密算法 hMatcher.setHashAlgorithmName("MD5"); //设置加密次数 //hMatcher.setHashIterations(5); super.setCredentialsMatcher(hMatcher); } }
认证流程图:
认证和记住我:
subject.isAuthenticated() 表示用户进行了身份验证登录的,即使有Subject.login 进行了登录
subject.isRemembered() 表示用户是通过 记住我 登录的,此时可能不是真正的你(如果你的朋友使用了你的电脑,或者你的Cookie被窃取)在访问的
两者二选一。即subject.isAuthenticate()==true,则subject.isRemembered()==false,反之一样。
流程解析:
当客户端访问服务器时,DelegatingFilterProxy 对象会拦截所有的请求路径,并把它交给ShiroFilterFactoryBean对象处理,ShiroFilterFactoryBean对象中注入了SecurityMananger对象,负责调用相关组件,实现用户身份认证,缓存,会话管理等功能。
ShiroFilterFactoryBean对象中还配置了FilterChainDefinitionMap这个参数,这个参数其实也相当于一个过滤器,他定义了哪些资源外部可以直接进行访问,哪些资源需要认证之后才能访问。ShiroFilterFactoryBean对象还对LoginUrl这个参数进行了设置,这个参数主要意义为:FilterChainDefinitionMap拦截的请求,直接重定向到LoginUrl这个参数配置的路径进行访问。