一:什么是Shiro?
它是一个安全框架,用于解决系统的认证和授权问题,同时提供了会话管理,数据加密机制。
二.应用程序如何使用Shiro框架?
可以看出,程序员只关注两部分:
1.如何获得Subject
2.如何定义一个符合规定的Realm域(密码比较器的定义也是程序员干的)
三.具体步骤:
1.导入jar包
Maven工程
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.2.3</version>
</dependency>
Web工程
2.过滤器的配置
3.产生代理类的方式
下面这一行代码,放在applicationContext.xml事务管理器声明之前
<aop:aspectj-autoproxy proxy-target-class="true" />
4.Shiro的配置文件application-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"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<description>Shiro的配置</description>
<!-- SecurityManager配置 -->
<!-- 配置Realm域 -->
<!-- 密码比较器 -->
<!-- 代理如何生成? 用工厂来生成Shiro的相关过滤器-->
<!-- 配置缓存:ehcache缓存 -->
<!-- 安全管理 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- Single realm app. If you have multiple realms, use the 'realms' property instead. -->
<property name="realm" ref="authRealm"/><!-- 引用自定义的realm -->
<!-- 缓存 -->
<property name="cacheManager" ref="shiroEhcacheManager"/>
</bean>
<!-- 自定义权限认证 -->
<bean id="authRealm" class="cn.itcast.jk.shiro.AuthRealm">
<property name="userService" ref="userService"/>
<!-- 自定义密码加密算法 -->
<property name="credentialsMatcher" ref="passwordMatcher"/>
</bean>
<!-- 设置密码加密策略 md5hash -->
<bean id="passwordMatcher" class="cn.itcast.jk.shiro.CustomCredentialsMatcher"/>
<!-- filter-name这个名字的值来自于web.xml中filter的名字 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<!--登录页面 -->
<property name="loginUrl" value="/index.jsp"></property>
<!-- 登录成功后 -->
<property name="successUrl" value="/home.action"></property>
<property name="filterChainDefinitions">
<!-- /**代表下面的多级目录也过滤 -->
<value>
/index.jsp* = anon
/home* = anon
/sysadmin/login/login.jsp* = anon
/sysadmin/login/logout.jsp* = anon
/login* = anon
/logout* = anon
/components/** = anon
/css/** = anon
/images/** = anon
/js/** = anon
/make/** = anon
/skin/** = anon
/stat/** = anon
/ufiles/** = anon
/validator/** = anon
/resource/** = anon
/** = authc
/*.* = authc
</value>
</property>
</bean>
<!-- 用户授权/认证信息Cache, 采用EhCache 缓存 -->
<bean id="shiroEhcacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:ehcache-shiro.xml"/>
</bean>
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- 生成代理,通过代理进行控制 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true"/>
</bean>
<!-- 安全管理器 -->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
</beans>
5.在applicationContext.xml文件中加载shiro配置文件
<import resource="classpath:spring/applicationContext-shiro.xml"></import>
6.编写密码比较器
1. 替换原有的utils工具包中的类
2. Md5Hash算法介绍
//高强度加密算法,不可逆
public static String md5(String password, String salt){
return new Md5Hash(password,salt,2).toString();
}
3.密码比较器
public class CustomCredentialsMatcher extends SimpleCredentialsMatcher {
/**
* 执行密码比较的方法
* 如果返回true,证明密码比较成功
* false,证明密码比较失败
*
* AuthenticationToken token:代表用户在界面上输入的用户名和密码
* AuthenticationInfo:它可以保存用户在数据库中的密文
*/
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
//1.向下转型
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
//2.得到原始密码,没有的密码,
String oldPwd = new String(upToken.getPassword());
//3.并进行加密
String newPwd = Encrypt.md5(oldPwd, upToken.getUsername());
//4.获取数据库中当前用户的密文
Object dbPwd = info.getCredentials();
return equals(newPwd, dbPwd);
}
}
7.编写自定义Realm域
public class AuthRealm extends AuthorizingRealm {
private UserService userService;//注入业务逻辑
public void setUserService(UserService userService) {
this.userService = userService;
}
//授权 当jsp页面上读取到shiro标签时,就会执行这个方法
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
System.out.println("授权");
//1.通过指定realm域的名字,找到之前放进去的principal值,这个值 就是用户对象
User user = (User) principal.fromRealm(this.getName()).iterator().next();
List permissions = new ArrayList();
//2.数据加载
Set<Role> roles = user.getRoles();//通过关联级别的数据加载,得到当前用户的所有角色
for(Role role :roles){
//遍历每个角色
Set<Module> modules = role.getModules();//再次使用关联级别数据检索,来加载当前角色下的模块(权限)
for(Module m:modules){
permissions.add(m.getCpermission());//组织权限字符串
}
}
//返回值
SimpleAuthorizationInfo sAuthInfo = new SimpleAuthorizationInfo();
sAuthInfo.addStringPermissions(permissions);
return sAuthInfo;
}
//认证 (登录)
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
System.out.println("认证");
//1.转化token
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
//2.调用业务方法,进行查询
List<User> list = userService.find("from User u where u.userName=?" , User.class, new Object[]{upToken.getUsername()});//根据用户名得到一个用户对象
User user = null;
if(list!=null && list.size()>0){
user = list.get(0);
}
return new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());
}
}
8.登录操作
if(UtilFuns.isEmpty(username)){
return "login";
}
//使用Shiro实现登录操作
try {
//1.得到Subject对象
Subject subject = SecurityUtils.getSubject();
//2.封装用户数据成一个AuthenicationToken对象
UsernamePasswordToken uptoken = new UsernamePasswordToken(username, password);
//3.实现登录操作
subject.login(uptoken); //立即调用AuthRealm域中的认证方法
//4.登录成功后,就可以从Shiro中取出用户对象
User user = (User)subject.getPrincipal();
//因为这些数据,在多处要使用,所以懒加载没有优势,所以提前加载出来,这样在多处使用时就不需要加载了
System.out.println(user.getDept().getDeptName());
Set<Role> roles = user.getRoles();//通过关联级别的数据加载,得到当前用户的所有角色
for(Role role :roles){
//遍历每个角色
System.out.println(role.getName());
Set<Module> modules = role.getModules();
for(Module m:modules){
System.out.println(m.getCpermission());
}
}
//5.将用户信息,放入session域中
session.put(SysConstant.CURRENT_USER_INFO, user);
} catch (Exception e) {
e.printStackTrace();
//当密码比较失败后,也会抛出异常
request.put("errorInfo", "对不起,用户名或密码错误,登录失败");
return "login";
}
return SUCCESS;
}
9.jsp
当jsp页面上出现Shiro标签时就会执行AuthRealm域中的授权方法。
1.引入Shiro标签
<%@ taglib uri=”http://shiro.apache.org/tags” prefix=”shiro” %>
登录过程:
授权的过程: