您的位置:首页 > 编程语言 > Java开发

SpringMVC+Spring+Hibernate+Mybatis+Shiro等整合及开发(3)

2018-03-16 21:16 477 查看
shiro简介
    一 Apache Shiro 是 Java 的一个安全框架。目前,使用 Apache Shiro 的人越来越多,因为它相当简单,对比 Spring Security,可能没有 Spring Security 做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的 Shiro 就足够了。对于它俩到底哪个好,这个不必纠结,能更简单的解决项目问题就好了。

    功能内部图:


Subject:主体,可以看到主体可以是任何可以与应用交互的 “用户”;
SecurityManager:相当于 SpringMVC 中的 DispatcherServlet 或者 Struts2 中的 FilterDispatcher;是 Shiro 的心脏;所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理。
Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得 Shiro 默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
Authrizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
Realm:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是 JDBC 实现,也可以是 LDAP 实现,或者内存实现等等;由用户提供;注意:Shiro 不知道你的用户 / 权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的 Realm;
SessionManager:如果写过 Servlet 就应该知道 Session 的概念,Session 呢需要有人去管理它的生命周期,这个组件就是 SessionManager;而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境、EJB 等环境;所有呢,Shiro 就抽象了一个自己的 Session 来管理主体与应用之间交互的数据;这样的话,比如我们在 Web 环境用,刚开始是一台 Web 服务器;接着又上了台 EJB 服务器;这时想把两台服务器的会话数据放到一个地方,这个时候就可以实现自己的分布式会话(如把数据放到 Memcached 服务器);
SessionDAO:DAO 大家都用过,数据访问对象,用于会话的 CRUD,比如我们想把 Session 保存到数据库,那么可以实现自己的 SessionDAO,通过如 JDBC 写到数据库;比如想把 Session 放到 Memcached 中,可以实现自己的 Memcached SessionDAO;另外 SessionDAO 中可以使用 Cache 进行缓存,以提高性能;
CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能
Cryptography:密码模块,Shiro 提高了一些常见的加密组件用于如密码加密 / 解密的。

二 再来看一个shiro认证流程:
    首先发出验证请求给Subject.login(用户名密码),Subject委托给SecurityManager验证,SecurityManager要拿到比较的对象,就向Realm索取,Realm是一般是自己定义的,我是从数据库里面取的。取出来返回回去,进行对比。



PS:上面有一部分内容摘自网上。
    经过上面的介绍知道,要实现简单的shiro应用我们着重关注:Realm数据获取。
但是我想实现的是基于url的权限控制;又自定义了鉴权filter。
    三.spring和shiro整合并自定义鉴权和Realm
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!-- Shiro生命周期处理器-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

<!--shiro web过滤bean-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 -->
<property name="loginUrl" value="/login.do" />
<!-- 认证成功统一跳转到first.action,建议不配置,shiro认证成功自动到上一个请求路径 -->
<!-- <property name="successUrl" value="/first.action"/> -->
<!-- 通过unauthorizedUrl指定没有权限操作时跳转页面-->
<property name="unauthorizedUrl" value="/refuse.jsp" />

<property name="filters">
<util:map>
<!--权限控制自定义filter-->
<entry key="perms" value-ref="urlAuthorizationFilter" />
</util:map>
</property>

<!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
<property name="filterChainDefinitions">
<value>
<!-- 对静态资源设置访问,不然都拦截了 -->
/images/** = anon
/js/** = anon
/css/** = anon
/dologin* = anon

<!-- 请求logout.action地址,shiro清除session -->
/logout.do=logout
<!-- /** = authc所有url都可以认证通过才能访问 -->
/a.do = perms
/** = authc
<!-- /** = anon所有url都可以匿名访问 -->
<!--/** = anon-->
</value>
</property>
</bean>

<!--自定义权限控制filter-->
<bean id="urlAuthorizationFilter" class="com.yanghs.shiro.filter.UrlAuthorizationFilter"/>

<!-- securityManager安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!--自定义用户信息获取器-->
<property name="realm" ref="userRealm" />
<!--session管理器-->
<property name="sessionManager" ref="sessionManager"/>
<!-- 注入缓存管理器 -->
<property name="cacheManager" ref="cacheManager"/>

</bean>

<!-- 自定义的realm --&
b0ec
gt;
<bean id="userRealm" class="com.yanghs.shiro.realm.UserRealm">
<!-- 将凭证匹配器设置到realm中,realm按照凭证匹配器的要求进行散列 -->
<!--<property name="credentialsMatcher" ref="credentialsMatcher"/>-->
<!--开启用户缓存-->
<property name="authenticationCachingEnabled" value="false"/>
<!--用户缓存-->
<!--<property name="authenticationCacheName" value=""/>-->
<!--开启权限缓存-->
<property name="authorizationCachingEnabled" value="true"/>
<!--权限缓存-->
<property name="authorizationCacheName" value="authenticationCache"/>
</bean>

<!-- 散列加盐凭证匹配器 -->
<!--<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="md5" />
<property name="hashIterations" value="1" />
</bean>-->

<!-- 缓存管理器 -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
</bean>

<!--session管理器-->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<!--多久检查session 的有效性 1800000毫秒 半小时-->
<property name="sessionValidationInterval" value="1800000"/>
<!--session 有效时间-->
<property name="globalSessionTimeout" value="1800000"/>

<!--session 监听器 可以设置多个-->
<property name="sessionListeners">
<util:list>
<ref bean="sessionListener"/>
</util:list>
</property>
<!-- 间隔多少时间检查,不配置是60分钟 -->
<!--<property name="sessionValidationScheduler" ref="sessionValidationScheduler"/>-->
<!-- 是否开启 检测,默认开启 -->
<property name="sessionValidationSchedulerEnabled" value="true"/>
<!-- 是否删除无效的,默认也是开启 -->
<property name="deleteInvalidSessions" value="true"/>
<!-- 会话Cookie模板 -->
<property name="sessionIdCookie" ref="simpleCookie"/>

</bean>

<!--session监听器-->
<bean id="sessionListener" class="com.yanghs.shiro.listener.MySessionListener"></bean>

<!--会话session id 生成器-->
<bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>

<!--会话Cookie模板-->
<bean id="simpleCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<!--cookie name-->
<constructor-arg value="JSESSIONID"/>
<!--如果设置为 true,则客户端不会暴露给客户端脚本代码,使用 HttpOnly cookie
有助于减少某些类型的跨站点脚本攻击;
此特性需要实现了 Servlet 2.5 MR6 及以上版本的规范的 Servlet 容器支持;-->
<property name="httpOnly" value="true"/>
<!--cookie的最大时间 关闭浏览器失效 设置时间的话为秒-->
<property name="maxAge" value="-1"/>
</bean>

</beans>自定义Realmpackage com.yanghs.shiro.realm;

import com.yanghs.common.entity.hbm.*;
import com.yanghs.common.service.IRoleService;
import com.yanghs.common.service.IUserService;
import com.yanghs.shiro.token.UserToken;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.Iterator;

/**
* @author yanghs
* @Description:自定义数据获取
* @date 2018/3/1 13:57
*/
public class UserRealm extends AuthorizingRealm {
@Resource(name = "userService")
IUserService userService;
@Resource(name = "roleService")
IRoleService roleService;
/**
* 授权逻辑获取
* @param principalCollection
* @return
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
Userinfo userinfo = (Userinfo) principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo sa = new SimpleAuthorizationInfo();
try {
Collection<Authority> authorityCollection = roleService.getRoleByUser(userinfo);
Iterator<Authority> authorityIterator = authorityCollection.iterator();
while (authorityIterator.hasNext()){
Authority authority = authorityIterator.next();
sa.addStringPermission(authority.getUrl());
}

} catch (Exception e) {
e.printStackTrace();
}
return sa;
}

/**
* 用户登录逻辑实现
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UserToken token = (UserToken) authenticationToken;
Userinfo userInfo = null;
try {
userInfo = userService.getUser(new Userinfo(null,token.getUsername(),null,null));
} catch (Exception e) {
e.printStackTrace();
}
if(userInfo == null){
throw new AccountException("用户不存在");
}
if(!userInfo.getPassword().equals(token.getPwd())){
throw new AccountException("用户密码错误");
}
AuthenticationInfo authcinfo = new SimpleAuthenticationInfo(userInfo,userInfo.getPassword(),this.getName());
return authcinfo;

}

/**
* 清除认证缓存
*/
public void clearCachedAuthenticationInfo() {
super.clearCachedAuthenticationInfo(SecurityUtils.getSubject().getPrincipals());
}

/**
* 清除权限缓存
*/
public void clearCachedAuthorizationInfo() {
super.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals());
}
}鉴权filterpackage com.yanghs.shiro.filter;

import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
* @author yanghs
* @Description:重写权限控制filter让其支持url权限控制
* @date 2018/3/5 14:06
*/
public class UrlAuthorizationFilter extends PermissionsAuthorizationFilter {
@Override
public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String path = httpServletRequest.getContextPath();
String uri = httpServletRequest.getRequestURI();
uri = uri.substring(path.length(),uri.length());

String[] perms = new String[1];
perms[0] = uri;
return super.isAccessAllowed(request, response, perms);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: