相信有很多的程序员,不愿意进行用户管理这块代码实现。

原因之一,不同的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 实践的更多相关文章

  1. Spring MVC中,事务是否可以加在Controller层

    一般而言,事务都是加在Service层的,但是爱钻牛角尖的我时常想:事务加在Controller层可不可以.我一直试图证明事务不止可以加在Service层,还可以加在Controller层,但是没有找 ...

  2. Spring MVC 学习总结(十)——Spring+Spring MVC+MyBatis框架集成(IntelliJ IDEA SSM集成)

    与SSH(Struts/Spring/Hibernate/)一样,Spring+SpringMVC+MyBatis也有一个简称SSM,Spring实现业务对象管理,Spring MVC负责请求的转发和 ...

  3. JSR-303 Bean Validation 介绍及 Spring MVC 服务端验证最佳实践

    任何时候,当要处理一个应用程序的业务逻辑,数据校验是你必须要考虑和面对的事情. 应用程序必须通过某种手段来确保输入参数在上下文来说是正确的. 分层的应用在很多时候,同样的数据验证逻辑会出现在不同的层, ...

  4. Spring MVC中@RequestMapping注解使用技巧(转)

    @RequestMapping是Spring Web应用程序中最常被用到的注解之一.这个注解会将HTTP请求映射到MVC和REST控制器的处理方法上. 在这篇文章中,你将会看到@RequestMapp ...

  5. Spring mvc中@RequestMapping 6个基本用法

    Spring mvc中@RequestMapping 6个基本用法 spring mvc中的@RequestMapping的用法.  1)最基本的,方法级别上应用,例如: Java代码 @Reques ...

  6. spring mvc中使用freemark的一点心得

    参考文档: FreeMarker标签与使用 连接http://blog.csdn.net/nengyu/article/details/6829244 freemarker学习笔记--指令参考: ht ...

  7. Http请求中Content-Type讲解以及在Spring MVC中的应用

    引言: 在Http请求中,我们每天都在使用Content-type来指定不同格式的请求信息,但是却很少有人去全面了解content-type中允许的值有多少,这里将讲解Content-Type的可用值 ...

  8. Spring mvc中@RequestMapping 6个基本用法小结(转载)

    小结下spring mvc中的@RequestMapping的用法. 1)最基本的,方法级别上应用,例如: @RequestMapping(value="/departments" ...

  9. Spring MVC中处理静态资源的多种方法

    处理静态资源,我想这可能是框架搭建完成之后Web开发的”头等大事“了. 因为一个网站的显示肯定会依赖各种资源:脚本.图片等,那么问题来了,如何在页面中请求这些静态资源呢? 还记得Spring MVC中 ...

随机推荐

  1. eclipse新发现功能之dos和terminal(ssh连接)

    dos功能: window——>show view——>other——>remote systems,选择remote shell,选择确定或者双击,打开了一个新工具窗口. 点击re ...

  2. JAVA之带转义字符的json字符串解析

    Json数据: { "ret": "0", "Weathers": "[{\"date\":\"20 ...

  3. C# 获取文件的MIME类型

    在C#中获取文件的MIME类型(Content Type)的方法如下 一.使用MimeMapping类 在System.Web程序集中,当前为静态类,就一个获取 // // 摘要: // 映射文档扩展 ...

  4. LaTeX技巧:LaTeX括号总结

    功能 语法 显示 不好看     ( \frac{1}{2} ) 好一点 \left( \frac{1}{2} \right) 您可以使用\left和\right来显示不同的括号: 功能 语法 显示 ...

  5. delete method not allowed 405错误

    造成该问题的原因:iis版本问题 解决办法如下: 修改配置文件web.config <system.webServer><modules><remove name=&qu ...

  6. Go语言之高级篇beego框架之参数配置与路由配置

    一.参数配置 beego默认会解析当前应用下的conf/app.conf文件 1.1.beego的参数配置 appname = WEB httpport = 8080 runmode = dev 几种 ...

  7. VS2010 SP1安装失败之”此计算机的状态不支持此安装“()

    升级安装VS2010SP1的时候,出现“此计算机的状态不支持此安装”,Google得之: 如下图显示: 安装程序已经检测到,此计算机不满足安装此软件所需的条件.必须先解决以下这些造成阻止的问题,才可以 ...

  8. [leetcode]Distinct Subsequences @ Python

    原题地址:https://oj.leetcode.com/problems/distinct-subsequences/ 题意: Given a string S and a string T, co ...

  9. List 转 ObservableCollection

    ObservableCollection<UserInfo> oc = new ObservableCollection<UserInfo>(); ls.ForEach(x = ...

  10. vim上次和下次光标位置

    ctrl + I 下次光标位置 ctrl + o上次光标位置.