Apache Shiro 是一个安全认证框架,和 Spring Security 相比,在于他使用了比较简洁易懂的认证和授权方式。其提供的 native-session(即把用户认证后的授权信息保存在其自身提供Session 中)机制,这样就可以和 HttpSession、EJB Session Bean 的基于容器的 Session 脱耦,和客户端应用、Flex 应用、远程方法调用等都可以使用它来配置权限认证。在exit-web-framework 里的 vcs-admin 例子用到该框架,具体使用说明可以参考官方帮助文档。

在这里主要讲解如何与 Spring 结合、以及认证、授权。

Apache Shiro 结合 Spring Shiro 拥有对 Spring Web 应用程序的一流支持。在 Web 应用程序中,所有 Shiro 可访问的万恶不请求必须通过一个主要的 Shiro 过滤器。该过滤器本身是极为强大的,允许临时的自定义过滤器链基于任何 URL 路径表达式执行。在 Shiro 1.0 之前,你不得不在 Spring web 应用程序中使用一个混合的方式,来定义 Shiro 过滤器及所有它在 web.xml 中的配置属性,但在 Spring XML 中定义 SecurityManager。这有些令人沮丧,由于你不能把你的配置固定在一个地方,以及利用更为先进的 Spring 功能的配置能力,如PropertyPlaceholderConfigurer 或抽象 bean 来固定通用配置。现在在 Shiro 1.0 及以后版本中,所有 Shiro 配置都是在 Spring XML 中完成的,用来提供更为强健的 Spring 配置机制。 Shiro框架执行流程与原理详细图解如下图所示:

1. 导入 jar 包(Eclipse 或 IDEA 基于 Spring 创建 Maven 工程这里就不做演示):

<!-- apache shiro dependencies -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.2.3</version>
</dependency>

2. 以下是如何在基于 Spring web 应用程序中配置 Shiro 核心控制器(web.xml):

<!-- Shiro核心控制器,一定放在struts2过滤器之前  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>

3. 产生代理类的方式:

下面这行代码放置在 applicationContext.xml 的事务管理器声明之前。

<!-- 告诉spring生成shiro代理子类时,采用cglib方式生成 -->
<aop:aspectj-autoproxy proxy-target-class="true" />

4. Shiro 的 bean 配置文件:

<description>Shiro与Spring整合</description>
<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 -->
</bean> <!-- 自定义Realm域的编写 -->
<bean id="authRealm" class="cn.itcast.shiro.AuthRealm">
<!-- 注入自定义的密码比较器 -->
<property name="credentialsMatcher" ref="customerCredentialsMatcher" ></property>
</bean> <!-- 自定义的密码比较器 -->
<bean id="customerCredentialsMatcher" class="cn.itcast.shiro.CustomerCredentialsMatcher"></bean> <!-- 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/loginAction_logout* = anon
/login* = anon
/logout* = anon
/components/** = anon
/css/** = anon
/img/** = anon
/js/** = anon
/plugins/** = anon
/images/** = anon
/js/** = anon
/make/** = anon
/skin/** = anon
/stat/** = anon
/ufiles/** = anon
/validator/** = anon
/resource/** = anon
/** = authc
/*.* = authc </value>
</property>
</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>

5. 在 applicationContext.xml 中加载 Shiro 配置文件:

<!--组装其它 配置文件-->
<import resource="classpath:spring/applicationContext-shiro.xml"/>

自定义 AuthRealm 类:

/**
*Realm域的类需要继承AuthorizingRealm
*/
public class AuthRealm extends AuthorizingRealm {
@Autowired
private UserService userService; //授权
/**
* 这个方法在什么时候调用?当jsp页面上第一次出现shiro标签时就会自动调用
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
//1.从Shiro中取出用户对象
User user = (User)pc.fromRealm(this.getName()).iterator().next(); List<String> permission = new ArrayList<String>();
//2.通过对象关联查询,加载用户的角色列表
Set<Role> roles = user.getRoles();
//3.遍历角色列表,得到每个角色
for (Role role : roles) {
Set<Module> modules = role.getModules();
//对象导航查询,模块列表
for (Module module : modules) {
if(!permission.contains(module)){
permission.add(module.getName());
} }
}
//生成一个返回值的对象
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermissions(permission);
return info;
} //认证 token参数代表用户在界面上输入的用户名和密码
/**
* 如果返回值为null,shiro就会抛出异常
*
* 如果返回值不为null,程序就会自动调用密码比较器
*
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { //1.向下转型
final UsernamePasswordToken upToken = (UsernamePasswordToken) token;
//2.调用业务方法,实现根据用户名进行查询
Specification<User> spec = new Specification<User>() {
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
return cb.equal(root.get("userName").as(String.class), upToken.getUsername());
}
};
List<User> userList = userService.find(spec);
//3.判断
if(userList!=null && userList.size()>0){
User user = userList.get(0);
//三个参数,第一个参数Principal代表用户对象,第二个参数credentials 代表密码 第三个参数realmName代表realm域的名字
return new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
}
return null;
} }

自定义 CustomerCredentialsMatcher 密码比较器类:

/**
* 密码比较器
*/
public class CustomerCredentialsMatcher extends SimpleCredentialsMatcher {
/**
* 执行密码比较
* 第一个参数:用户在界面上输入的用户名和密码
* 第二个参数info:代表用户的全部信息
*
* 返回值:
* true代表密码比较成功
* false代表密码比较失败,Shiro就会抛出异常
*/
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
//1.向下转型
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
//2.调用Md5Hash算法 第一个参数:明文 第二个参数:盐 第三个参数:加盐的次数,代表轮换的次数
//String uipwdEncrypt = new Md5Hash(new String(upToken.getPassword()), upToken.getUsername(), 2).toString();
String uipwdEncrypt = Encrypt.md5(new String(upToken.getPassword()), upToken.getUsername());
//3.可以得到这个用户在数据库中的密文
String dbpwd = info.getCredentials().toString();
return equals(uipwdEncrypt, dbpwd);
} }

LoginAction 登陆 Action 类:

/**
* @Description: 登录和退出类
*
* 继承BaseAction的作用
* 1.可以与struts2的API解藕合
* 2.还可以在BaseAction中提供公有的通用方法
*/
@Namespace("/")
@Results({
@Result(name="login",location="/WEB-INF/pages/sysadmin/login/login.jsp"),
@Result(name="success",location="/WEB-INF/pages/home/fmain.jsp"),
@Result(name="logout",location="/index.jsp")})
public class LoginAction extends BaseAction {
private static final long serialVersionUID = 1L;
private String username;
private String password;
@Action("loginAction_login")
public String login() throws Exception {
//SSH传统登录方式
//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域中
session.put(SysConstant.CURRENT_USER_INFO, user); } catch (Exception e) {
e.printStackTrace();
request.put("errorInfo", "对不起,用户名或密码错误,登录失败!");
return "login";
}
return SUCCESS;
}
//退出
@Action("loginAction_logout") public String logout(){
session.remove(SysConstant.CURRENT_USER_INFO); //删除session
SecurityUtils.getSubject().logout(); //登出
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;
} }

Shiro 框架与 Spring 整合效果如下图所示:

1、Shiro 安全框架与Spring 整合详解的更多相关文章

  1. shiro安全框架和spring整合

    上干货......... 整合spring的配置文件 <?xml version="1.0" encoding="UTF-8"?><beans ...

  2. Java后端框架之Spring Boot详解,文末有Java分布式实战项目视频可取

    在 Java 后端框架繁荣的今天,Spring 框架无疑是最最火热,也是必不可少的开源框架,更是稳坐 Java 后端框架的龙头老大. 用过 Spring 框架的都知道 Spring 能流行是因为它的两 ...

  3. 8 -- 深入使用Spring -- 7...2 MVC框架与Spring整合的思考

    8.7.2 MVC 框架与Spring整合的思考 对于一个基于B/S架构的JAVA EE 应用而言,用户请求总是向MVC框架的控制器请求,而当控制器拦截到用户请求后,必须调用业务逻辑组件来处理用户请求 ...

  4. Maven环境下搭建SSH框架之Spring整合Hibernate

    © 版权声明:本文为博主原创文章,转载请注明出处 1.搭建环境 Spring:4.3.8.RELEASE Hibernate:5.1.7.Final MySQL:5.7.17 注意:其他版本在某些特性 ...

  5. idea spring+springmvc+mybatis环境配置整合详解

    idea spring+springmvc+mybatis环境配置整合详解 1.配置整合前所需准备的环境: 1.1:jdk1.8 1.2:idea2017.1.5 1.3:Maven 3.5.2 2. ...

  6. 【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

    Spring AOP详解 . JDK动态代理.CGLib动态代理  原文地址:https://www.cnblogs.com/kukudelaomao/p/5897893.html AOP是Aspec ...

  7. Spring配置文件详解 – applicationContext.xml文件路径

    Spring配置文件详解 – applicationContext.xml文件路径 Java编程                 spring的配置文件applicationContext.xml的默 ...

  8. spring事务详解(五)总结提高

    系列目录 spring事务详解(一)初探事务 spring事务详解(二)简单样例 spring事务详解(三)源码详解 spring事务详解(四)测试验证 spring事务详解(五)总结提高 一.概念 ...

  9. Spring学习(一)-----Spring 模块详解

    官方下载链接:http://repo.spring.io/release/org/springframework/spring/ Spring 模块详解: Core 模块 spring-beans-3 ...

随机推荐

  1. redis基础学习总结

    学习目标: 1.redis特点及安装     2.redis键值操作     3.redis数据类型[string, link,set,orderset,hash]     4.事务     5.消息 ...

  2. AWK入门

    AWK类型    AWK:源于AT&T实验室的AWK    NAWK:AWK的升级版    GAWK:GNU AWK,兼容AWK和NAWK    程序结构    BEGIN语句块,可选     ...

  3. VSTO:使用C#开发Excel、Word【16】

    使用工作表对象Worksheet对象表示Excel工作簿中的工作表.Worksheet有一个Name属性,返回工作表的名称(例如“Sheet1”). 工作表管理Worksheet对象具有一个Index ...

  4. BFC清除浮动

    BFC 就是清除浮动 用来处理文档脱离文档流的问题 清除浮动的方法: a.父元素也添加一个浮动 产生弊端就是:margin 不能使用 b.给父元素添加一个:display:inline-block 弊 ...

  5. 学号 20175223 《Java程序设计》第1周学习总结

    学号 20175223 <Java程序设计>第1周学习总结 教材学习内容总结 第一章要点: 要点1:Java的三大平台:Java SE,Java EE,Java ME. 要点2:Java的 ...

  6. python基础09_字符串格式化

    首先,使用%s 的方法. #!/usr/bin/env python # coding:utf-8 # 不用format方法,使用%s 和%d name = 'Tom' age = 100 msg = ...

  7. guava-retrying 源码解析(阻塞策略详解)

    这是一种策略,用于决定重试者应如何在重试尝试之间进行阻止.通常这只是一个thread.sleep(),但是如果需要的话,实现可能更复杂. 一.阻塞策略相关的类或接口 1.阻塞策略接口:BlockStr ...

  8. Centos yum 命令行 安装KDE Desktop

    1:修改yum源为本地源 (见相关随笔:centos 配置本地yum源) 2:# yum groupinstall "X Window System" ← 安装基本的X系统组件# ...

  9. 转发 C# Win32 API程序控制鼠标的操作

    命名空间:using System.Runtime.InteropServices; 在程序中添加: [DllImport("User32")]public extern stat ...

  10. GAN 教程记录

    目标:使G产生的分布sample出来接近D的分布 1.G产生的data是否是database中的图片 a.计算L1 L2相似度 2.GAN与其他生成器相比较,能够生成较为清晰的图片 3.一次itera ...