Spring MVC 急速集成 Shiro 实录
相信有很多的程序员,不愿意进行用户管理这块代码实现。
原因之一,不同的JavaEE 系统,用户管理都会有个性化的实现,逻辑很繁琐。
而且是系统门面,以后背锅的几率非常大,可谓是低收益高风险。
最近在系统中集成了 Shiro,感觉这个小家伙还是相当灵活的。
完善的用户认证和授权,干净的API,让人如沐春分。
Apache Shiro 作为一个强大而灵活的开源安全框架,它干净利落地处理身份认证,授权,企业会话管理和加密。
安全有时候是很复杂的,甚至是痛苦的,但它没有必要这样。框架应该尽可能掩盖复杂的地方,露出一个干净而直观的 API。
Apache Shiro 的首要目标是易于使用和理解。
以下是你可以用 Apache Shiro 所做的事情:
a.验证用户来核实他们的身份
b.对用户执行访问控制,如:
c.判断用户是否被分配了一个确定的安全角色。
d.判断用户是否被允许做某事。
e.在任何环境下使用 Session API,即使没有 Web 或 EJB 容器。
f.在身份验证,访问控制期间或在会话的生命周期,对事件作出反应。
g.聚集一个或多个用户安全数据的数据源,并作为一个单一的复合用户“视图”。
h.启用单点登录(SSO)功能。
i.并发登录管理(一个账号多人登录作踢人操作)。
j.为没有关联到登录的用户启用"Remember Me"服务。
…
以及更多——全部集成到紧密结合的易于使用的 API 中。
目前主流安全框架有 SpringSecurity 和 Shiro,相比于 SpringSecurity,Shiro 轻量化,简单容易上手。
SpringSecurity 太笨重了,难以上手,且只能在 Spring 里用,所以极力推荐Shiro。
本文重点描述集成过程,能让你迅速的将 Shiro 集成到 JavaEE 项目中,毕竟项目都挺紧张的。
1.前戏
Shiro 核心jar:
<!--权限控制 shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency> <dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>${shiro.version}</version>
</dependency>
JavaEE 应用开始的地方,web.xml 配置:
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Spring-Shiro-context 配置应用启动的使用,会帮助你做很多事情:
<!--Shiro 关键过滤器配置-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/sys/login"/> <!--请求 Url 为 get方式-->
<property name="successUrl" value="/sys/index"/>
<property name="filters">
<map>
<entry key="authc" value-ref="formAuthenticationFilter"/>
</map>
</property>
<property name="filterChainDefinitions" ref="shiroFilterChainDefinitions"/>
</bean> <!-- Shiro 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="systemAuthorizingRealm"/>
<property name="cacheManager" ref="shiroCacheManager"/>
</bean> <!--自定义系统认证域-->
<bean id="systemAuthorizingRealm" class="com.rambo.spm.core.shiro.SysAuthorizingRealm"/> <!--shiro ehcache缓存-->
<bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManager" ref="cacheManagerFactory"/>
</bean> <!--扩展表单认证过滤器-->
<bean id="formAuthenticationFilter" class="com.rambo.spm.core.shiro.FormAuthenticationFilter"/> <!--权限过滤链定义 -->
<bean name="shiroFilterChainDefinitions" class="java.lang.String">
<constructor-arg>
<value>
/static/** = anon
/captcha-image = anon
/sys/login = authc
/sys/logout = logout
/** =user
</value>
</constructor-arg>
</bean> <!--借助 SpringAOP 扫描那些使用 Shiro 注解的类-->
<aop:config proxy-target-class="true"/> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean> <!--用于在实现了Initializable/Destroyable接口的 Shiro bean 初始化时回调-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
2.个性化
请求发起的地方一般是前端,不管你是 .jsp/.php/.net.......方式都是类似
<html>
<body>
<h1>login page</h1>
<form id="" action="service/dologin" method="post">
<label>账号:</label><input name="userName" maxLength="40"/>
<input title="是否是管理员" type="checkbox" name="isAdmin"><label>是否为管理员</label><br>
<label>密码:</label><input title="密码" type="password" name="password" /><br>
<input type="submit" value="登录"/>
</form>
<%--用于输入后台返回的验证错误信息 --%>
<P><c:out value="${message }"/></P>
</body>
</html>
自定义项目验证域(验证域可以有多个,已可以有多种方式)。
public class SysAuthorizingRealm extends AuthorizingRealm {
    private Log log = LogFactory.get();
    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private SysRoleService sysRoleService;
    @Autowired
    private SysMenuService sysMenuService;
    /**
     * 获取当前用户的认证信息
     *
     * @param authcToken 携带用户认证所需的信息
     * @return 认证结果信息
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
        CaptchaUsernamePasswordToken spmToken = (CaptchaUsernamePasswordToken) authcToken;
        log.info("token:{}", ReflectionToStringBuilder.toString(spmToken));
        SysUser sysUser = sysUserService.getSysUserByLoginName(spmToken.getUsername());
        if (sysUser == null) {
            return null;
        }
        byte[] salt = Hex.decode(sysUser.getPasswd().substring(0, 16));
        return new SimpleAuthenticationInfo(new SpmPrincipal(sysUser), sysUser.getPasswd().substring(16), ByteSource.Util.bytes(salt), getName());
    }
    /**
     * 获取当前用户授权信息
     *
     * @param principals 该用户身份集合
     * @return 当前用户授权信息
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SpmPrincipal spmPrincipal = (SpmPrincipal) super.getAvailablePrincipal(principals);
        log.info("授权当前:{}", ReflectionToStringBuilder.toString(spmPrincipal));
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        List<SysRole> sysRoleList = sysUserService.listSysRoleByUserId(spmPrincipal.getId());
        for (SysRole sysRole : sysRoleList) {
            info.addRole(sysRole.getRoleType());
            List<SysMenu> sysMenuList = sysRoleService.listSysMenuByRoleId(sysRole.getUuid());
            for (SysMenu sysMenu : sysMenuList) {
                info.addStringPermission(sysMenu.getPermisson());
            }
        }
        info.addStringPermission("user");
        return info;
    }
    /**
     * 设定密码校验的Hash算法与迭代次数
     */
    @PostConstruct
    public void initCredentialsMatcher() {
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher("SHA-1");
        matcher.setHashIterations(1024);
        setCredentialsMatcher(matcher);
    }
}
请求的后台服务,在这里你可以在进行一点业务逻辑
/**
* shiro 登录请求控制,真正的请求由 shiroFilter --> formAuthenticationFilter 进行处理
* 登录成功: 跳转配置的 succssUrl,不触发该方法;
* 登录失败: 触发该方法,可以从扩展的 formAuthenticationFilter 中获取具体的错误信息;
*/
@PostMapping("/sys/login")
public Object postSysLogin(HttpServletRequest httpRequest, ModelAndView modelAndView) {
String exceptionName = (String) httpRequest.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME); String errorMsg = null;
if (IncorrectCaptchaException.class.getName().equals(exceptionName)) {
errorMsg = "验证码错误!";
} else if (UnknownAccountException.class.getName().equals(exceptionName)) {
errorMsg = "用户不存在!";
} else if (IncorrectCredentialsException.class.getName().equals(exceptionName)) {
errorMsg = "用户或密码错误!";
} else if (exceptionName != null && StrUtil.startWith(exceptionName, "msg:")) {
errorMsg = StrUtil.removeAll(exceptionName, "msg:");
}
return setModelAndView(modelAndView, "sys/sysLogin", errorMsg);
}
整个集成工作就结束了,是不是简单的不要不要的。
等下次项目经理指派你做用户管理的时候,只需要花半天的时间做设计。
借助Shiro 半天时间实现代码。
然后将剩下的时间,做做自己喜欢的其他研究工作。
Spring MVC 急速集成 Shiro 实录的更多相关文章
- 细说shiro之五:在spring框架中集成shiro
		
官网:https://shiro.apache.org/ 1. 下载在Maven项目中的依赖配置如下: <!-- shiro配置 --> <dependency> <gr ...
 - mybatis实战教程(mybatis in action)之六:与Spring MVC 的集成
		
前面几篇文章已经讲到了mybatis与spring 的集成.但这个时候,所有的工程还不是web工程,虽然我一直是创建的web 工程.今天将直接用mybatis与Spring mvc 的方式集成起来,源 ...
 - spring mvc mybatis集成踩的坑
		
开园这么多年了也没写几篇文章,现在想想光看别人的也不行啊,咱也自己写写,就写这天我我在做spring mvc与mybatis的集成时遇到的问题 1 spring与mybatis的集成 这个相信大家都弄 ...
 - mybatis :与Spring MVC 的集成
		
用mybatis与Spring mvc 的方式集成起来,源码在本文结尾处下载.主要有以下几个方面的配置1. web.xml 配置 spring dispatchservlet ,比如为:mvc-dis ...
 - Mybatis学习(6)与Spring MVC 的集成
		
前面几篇文章已经讲到了mybatis与spring 的集成.但这个时候,所有的工程还不是web工程,虽然我一直是创建的web 工程.今天将直接用mybatis与Spring mvc 的方式集成起来,源 ...
 - Java Spring MVC项目搭建(一)——Spring MVC框架集成
		
1.Java JDK及Tomcat安装 我这里安装的是JDK 1.8 及 Tomcat 8,安装步骤详见:http://www.cnblogs.com/eczhou/p/6285248.html 2. ...
 - Spring MVC 中急速集成 Shiro 实践
		
相信有很多的程序员,不愿意进行用户管理这块代码实现. 原因之一,不同的JavaEE 系统,用户管理都会有个性化的实现,逻辑很繁琐. 而且是系统门面,以后背锅的几率非常大,可谓是低收益高风险. 最近在系 ...
 - Spring mvc 中有关 Shiro 1.2.3 配置问题
		
Spring 版本:3.2.x, 4.0.x [问题说明] 首先介绍下配置出错情况: (1)项目中,Spring3 and Spring4 的 applicationContext.xml aop ...
 - springboot (spring mvc)集成swagger
		
最近用springboot构建rest接口,考虑到最方便的验证接口,想到了引入swagger. 基本的步骤大致如下: 1.pom中引入swagger依赖: <dependency> < ...
 
随机推荐
- ofbiz 本地化及邮件设置126邮箱
			
ofibz登陆功能有通过电子邮件找会密码的功能,但找回密码功能需要配置一个发送email的邮箱账号和smtp服务器的配置,具体配置如下: 1:在ofbiz数据库的表product_store_emai ...
 - ADO.Net(一)——增、删、改、查
			
数据访问 对应命名空间:System.Data.SqlClient; SqlConnection:连接对象 SqlCommand:命令对象 SqlDataReader:读取器对象 CommandTex ...
 - 说完Pivot 今天说下Unpivot 的处理方式
			
上次说到,既然有Pivot 的行转列,那么肯定也有Unpivot 的列转行 .其实unpivot 处理的情况也是差不多,也是分3步走. 首先也是先演示一下unpivot 的用法 ),Mon TIME, ...
 - 天朝使用GAE入门指南
			
0. 引言 Across the Great Wall, we can reach every corner in the world. 洒家最近玩了几下 Google App Engine.由于众所 ...
 - netfilter-在内核态操作网络数据包
			
一.概述 netfilter是自2.4内核的一个数据包过滤框架.可以过滤数据包,网络地址和端口转换 ...
 - 150922-写写博客监督下不自觉的自己-PPT,Linux,HTML
			
开始学PHP的日子里,总是懒散的有一天没一天的.无意间听闻写博客来展示代码(现在还远远做不到哇),来监督个人每天的学习进度,鉴于自己还是爱写一点文字,但愿可以坚持下去. 凡是都喜欢有个计划,骨子里的理 ...
 - 阿里云+wordpress搭建个人博客网站【小白专用的图文教程】
			
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...
 - NOIP2006金明的预算方案[DP 有依赖的背包问题]
			
题目描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间.更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”.今 ...
 - C# Interlocked 笔记
			
无锁代码下,在读写字段时使用内存屏障往往是不够的.在 64 位字段上进行加.减操作需要使用Interlocked工具类这样更加重型的方式.Interlocked也提供了Exchange和Compare ...
 - linux内核启动以及文件系统的加载过程
			
Linux 内核启动及文件系统加载过程 当u-boot 开始执行 bootcmd 命令,就进入 Linux 内核启动阶段.普通 Linux 内核的启动过程也可以分为两个阶段.本文以项目中使用的 lin ...