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. Failed to process import candidates for configuration class [com.simple.....]

    主要原因: 是因为自己定制的starter在打包时(package)用了spring-boot-maven-plugin,即在你的定制starter工程的pom.xml中有如下配置: <buil ...

  2. python安装pyMysql

    python2和python3是不兼容的,在python2中,链接数据库使用的是mysqldb,但在python3中是是pyMysql. 1.首先dos进入python安装目录,找到并进入Script ...

  3. CentOS7 linux下yum安装redis以及使用

    1.安装redis数据库 yum install redis 2.下载fedora的epel仓库 yum install epel-release 3.启动redis服务 systemctl star ...

  4. JAVA循环结构

    JAVA循环结构:顺序结构只能执行一次,如果要执行多次需要用到循环 JAVA中的循环结构有while:do...while:for: 1.while循环:先判断布尔表达式中的值,若为true,执行循环 ...

  5. LAMP架构(二)

    第十八次课 LAMP架构(二) 目录 一.Apache默认虚拟主机 二.Apache用户认证 三.域名跳转 四.Apache访问日志 五.访问日志不记录静态文件 六.访问日志切割 七.静态元素过期时间 ...

  6. noj最长公共子序列

    1041.最长公共子序列 时限:1000ms 内存限制:200000K  总时限:3000ms 描述 一个给定序列的子序列是在该序列中删去若干元素后得到的序列.确切地说,若给定序列X=<x1, ...

  7. linux c 输出信息到console

    static void console_log(const char *format, ...) { static FILE *fpConsole; if (fpConsole == NULL) { ...

  8. Power BI和 Visio 集成优缺点

    Power BI 的 Visio 自定义视觉,这个功能是非常值得让人兴奋的,小悦相信这是一个非常重要的开发,不仅适用于 Visio,也适用于Power BI.现在已经有越来越多的可视化,它们以更简洁的 ...

  9. Python2和Python3安装注意事项

    1. 到官网 https://www.python.org/downloads/windows/ 下载 Windows x86-64 executable installer版本: 2. python ...

  10. c语言笔记3运算符与表达式

    运算符与表达式 知识点一 操作数:参与运算的具体对象. 运算符:指明了对操作数进行的某项运算. 表达式:表示一个求值得规则.它由变量.常量.运算符和函数.括号按一定规则组成. 书写表达式的细节:1,运 ...