shiro是一个强大且易用的Java权限的框架
- shiro有四大基石
①身份验证—登陆
②授权:权限判断
③密码学:加密
④会话管理:session - shiro的架构
①subject:当前用户
②SecurityManager:shiro的权限管理器—核心对象
③realm:shiro从中获取数据 - 如何使用shiro
首先需要导入shiro的jar包
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>日志包
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
- 代码步骤
//需要先获取SecurityManager对象,通过工厂对象IniSecurityManagerFactory
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
//把SecurityManager对象设置到上下文,让所有地方都可调用
SecurityUtils.setSecurityManager(securityManager);
//获取当前用户或者是游客
Subject subject = SecurityUtils.getSubject();
//判断是否登陆,没有登陆就让他登陆
if (!subject.isAuthenticated()){
try {
//要登陆,先准备令牌,传入用户名和密码
UsernamePasswordToken token = new UsernamePasswordToken("root","92597");
//登陆
subject.login(token);
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println("用户名出错!!!");
//Incorrect(错误的)Credentials(凭证)Exception
}catch (IncorrectCredentialsException e) {
e.printStackTrace();
System.out.println("密码出错!!!");
}catch (AuthenticationException e){
e.printStackTrace();
System.out.println("出现了一个神密的错误!!!");
}
}
//是否登陆
System.out.println("是否登陆"+subject.isAuthenticated());
//角色判断
System.out.println("admin角色"+subject.hasRole("admin"));
//权限判断
System.out.println("有employee的权限"+subject.isPermitted("employee:save"));
//退出登陆
// subject.logout();
- 自定义realm —直接继承AuthorizingRealm,继承这个类后实现两个方法,
doGetAuthorizationInfo(授权) /doGetAuthenticationInfo(登录认证)
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//拿到Token ---只有这一个UsernamePasswordToken令牌,所以可以强转
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
//通过token拿到用户名 ---登陆时输入的用户名
String username = token.getUsername();
//通过用户名拿到数据库中的密码
String password = getByName(username);
if (password==null){
//返回空就是代表用户名不存在,shior会自动帮你报UnknownAccountException
return null;
}
//将前台传入的用户名,和数据库中的密码传入,SimpleAuthenticationInfo会自动帮我们判断是否正确---realmName:"名称随便取"
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username,password,"MyRealm");
return simpleAuthenticationInfo;
}
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//拿到当前用户名
String username = (String)principalCollection.getPrimaryPrincipal();
//根据用户名拿到对应角色与权限
Set<String> roles = getRolesByUsername(username);
Set<String> permissions = getPermissionsByUsername(username);
//拿到验证信息对象
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//设置角色
simpleAuthorizationInfo.setRoles(roles);
//设置权限
simpleAuthorizationInfo.setStringPermissions(permissions);
return simpleAuthorizationInfo;
}
- 密码加密
/**
*第一个参数是加密算法:MD5
*第二个参数是原始密码:admin
*第三个参数是盐值:zbj
*第四个参数是加密次数:6
*/
SimpleHash simpleHash = new SimpleHash("MD5","admin","zbj",6);
- 如果密码是加密之后的,那么在登陆判断时,也需要使用md5加密方式判断
①测试方法中,让自定义realm加上算法
//使用MD5加密方式判断密码
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();//密码匹配器
//设置加密方式
matcher.setHashAlgorithmName("md5");
//设置加密次数
matcher.setHashIterations(6);
//将匹配器交给realm
myRealm.setCredentialsMatcher(matcher);
②盐值判断需要在登陆验证方法中添加盐值
//加密验证的加盐
//准备盐值
ByteSource bytes = ByteSource.Util.bytes("zbj");
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username,password,bytes,"MyRealm");
shiro集成spring
- 导包
<!-- shiro的支持包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.4.0</version>
<type>pom</type>
</dependency>
<!-- shiro与Spring的集成包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
- 在web.xml中配置shiro的过滤器
这个过滤器并没有什么实际意义,真正的过滤器在spring的xml配置文件中,但是两个过滤器的名字必须相同
<!--shiro的过滤器,并没有什么作用,真正的过滤器在applicationContext-shiro.xml中-->
<filter>
<filter-name>shiroFilter</filter-name><!--这里的名字必须与真正的过滤器的名字相同-->
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<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>
- (3)配置applicationContext-shiro.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!--shiro的核心对象 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="aiSellRealm"/>
</bean>
<!--自定义的一个Realm-->
<bean id="aiSellRealm" class="cn.zr.aisell.shiro.AiSellRealm">
<property name="credentialsMatcher">
<!--hash散列 ,MD5加密 -->
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="MD5"/>
<property name="hashIterations" value="6"/>
</bean>
</property>
</bean>
<!--shiro的注解支持-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.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>
<!--真正的过滤器-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<!--没有登陆,跳转到这个路径-->
<property name="loginUrl" value="/login"/>
<!--登陆成功,跳转到这个路径-->
<property name="successUrl" value="/main"/>
<!--没有权限,跳转到这个路径-->
<property name="unauthorizedUrl" value="/s/unauthorized.jsp"/>
<property name="filterChainDefinitionMap" ref="filterChainDefinitionMapFactory">
</property>
</bean>
<bean id="filterChainDefinitionMapFactory" factory-bean="filterChainDefinitionMap" factory-method="createFilterChainDefinitionMap"/>
<bean id="filterChainDefinitionMap" class="cn.zr.aisell.shiro.FilterChainDefinitionMapBuilder"/>
</beans>
- 在核心配置文件applicationContext.xml中配置读取applicationContext-shiro.xml
<import resource="classpath:applicationContext-shiro.xml" />
- 把路径权限放入map集合中,并且是有序的map集合
新建一个类,并且这个类已经在shiro.xml中配置了bean,这样放行路径和权限路径就是动态的了
//LinkedHashMap是有顺序的
Map<String , String> map = new LinkedHashMap<>();
//存入路径与是否可以访问
//这个是放行路径
map.put("/s/login.jsp","anon");
map.put("/login","anon");
//放行easyui,js的样式
map.put("/easyui/**","anon");
map.put("/js/**","anon");
map.put("/images/**","anon");
map.put("/css/**","anon");
map.put("/fonts/**","anon");
//需要只有相应的权限才能访问的路径
map.put("/employee/index","perms[employee:index]");
//必须登陆才能访问的路径
map.put("/**","authc");
return map;