什么是shiro
Apache Shiro是Java的一个安全框架。功能强大,使用简单的Java安全框架,它为开发人员提供一个直观而全面的认证,授权,加密及会话管理的解决方案。
准备工作
maven工程的配置
<!-- shiro -->
<!-- apache shiro dependencies -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>${shiro.version}</version>
</dependency>
web工程的导包
1. 在web.xml文件中的配置
<!-- 注意:shiro的filter必须在struts2的filter之前,否则action无法创建 -->
<!-- Shiro Security filter filter-name这个名字的值将来还会在spring中用到 -->
<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>
注意:shiro的filter必须在struts2的filter之前,否则action无法创建
2. 在applicationContext.xml的配置
<!--Shiro安全框架产生代理子类的方式: 使用cglib方式,放在事务管理器之前配置-->
<aop:aspectj-autoproxy proxy-target-class="true" />
注意:放在事务管理器之前配置
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"
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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.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="com.dml.bs.shiro.AuthRealm">
<property name="userService" ref="userService"/>
<!-- 自定义密码加密算法 -->
<property name="credentialsMatcher" ref="passwordMatcher"/>
</bean>
<!-- 设置密码加密策略 md5hash -->
<bean id="passwordMatcher" class="com.dml.bs.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>
注意:几处的修改
ehcache-shiro.xml
跳转的页面可以自行修改
4.ehcache-shiro.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" name="shiroCache">
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
/>
</ehcache>
5.加密算法
public class Encrypt {
//高强度加密算法,不可逆
public static String md5(String password, String salt){
return new Md5Hash(password,salt,2).toString();
}
public static void main(String[] args) {
System.out.println(new Md5Hash("123456","tony",2).toString());
}
}
6.AuthRealm
public class AuthRealm extends AuthorizingRealm{
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
//授权 当jsp页面出现shiro标签时,就会执行授权方法
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
System.out.println("授权");
User user = (User) pc.fromRealm(this.getName()).iterator().next();//根据realm的名字去找对应的realm
Set<Role> roles = user.getRoles();//对象导航
List<String> permissions = new ArrayList<>();
for (Role role : roles) {
//遍历每个角色
Set<Module> modules = role.getModules();//得到每个角色下的模块列表
for (Module module : modules) {
permissions.add(module.getName());
}
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermissions(permissions);//添加用户的模块(权限)
return info;
}
//认证 token代表用户在界面输入的用户名和密码
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("认证");
//向下转型
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
//调用业务方法,实现根据用户名查询
String hql = "from User where userName = ?";
List<User> list = userService.find(hql, User.class, new String[]{upToken.getUsername()});
if(list != null && list.size() >0){
User user = list.get(0);
AuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
return info; //此处如果返回,就会立即进入密码比较器
}
return null;
}
}
7.CustomCredentialsMatcher
public class CustomCredentialsMatcher extends SimpleCredentialsMatcher{
//密码比较的方法 token代表用户在界面输入的用户名和密码 info代表从数据库中得到加密数据
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
//向下转型
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
//将用户在界面输入原始密码加密upToken.getPassword().toString()返回数组的首地址
Object pwd = Encrypt.md5(new String(upToken.getPassword()), upToken.getUsername());
//取出数据库中的加密密码
Object dbPwd = info.getCredentials();
return this.equals(pwd, dbPwd);
}
}
LoginAction
public class LoginAction extends BaseAction {
private static final long serialVersionUID = 1L;
private String username;
private String password;
//SSH传统登录方式
public String login() throws Exception {
// if(true){
// String msg = "登录错误,请重新填写用户名密码!";
// this.addActionError(msg);
// throw new Exception(msg);
// }
// User user = new User(username, password);
// User login = userService.login(user);
// if (login != null) {
// ActionContext.getContext().getValueStack().push(user);
// session.put(SysConstant.CURRENT_USER_INFO, login); //记录session
// return SUCCESS;
// }
// return "login";
if(UtilFuns.isEmpty(username))
return "login";
try {
//1.得到Subject
Subject subject = SecurityUtils.getSubject();
//2.调用登录方法
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//当这行代码执行时,就会进入密码比较器
subject.login(token);
//3.登录成功时,就从Shiro中取出用户的登录信息
User user = (User) subject.getPrincipal();
//4.将用户放入设施上域中
session.put(SysConstant.CURRENT_USER_INFO, user);
} catch (Exception e) {
e.printStackTrace();
request.put("errorInfo", "对不起,用户名或密码错误!");
return "login";
}
return SUCCESS;
}
//退出
public String logout(){
session.remove(SysConstant.CURRENT_USER_INFO); //删除session
return "logout";
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
登陆后的页面加上如下代码,就会执行授权
<!-- 当jsp页面碰到shiro标签时就执行AuthRealm中授权方法 -->
<shiro:hasPermission name="系统首页">
<span id="topmenu" onclick="toModule('home');">系统首页</span><span id="tm_separator"></span>
</shiro:hasPermission>
<shiro:hasPermission name="系统管理">
<span id="topmenu" onclick="toModule('sysadmin');">系统管理</span>
</shiro:hasPermission>
<shiro:hasPermission name="素质学分任务管理">
<span id="topmenu" onclick="toModule('cargo');">素质学分任务管理</span><span id="tm_separator"></span>
</shiro:hasPermission>
<shiro:hasPermission name="学生任务管理">
<span id="topmenu" onclick="toModule('baseinfo');">学生任务管理</span><span id="tm_separator"></span>
</shiro:hasPermission>
<shiro:hasPermission name="素质学分任务审核">
<span id="topmenu" onclick="toModule('stat');">素质学分任务审核</span><span id="tm_separator"></span>
</shiro:hasPermission>