赞
踩
Apache Shiro 是一个强大而灵活的开源安全框架,从官网上,我们基本上可以了解到,她提供的服务非常明确:
1.Authentication(认证)
2.Authorization(授权)
3.Session Management(会话管理)
4.Cryptography(加密)
<!--处理登录相关依赖包-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-cas</artifactId>
<version>1.2.0</version>
</dependency>
<!--shiro过滤-->
<filter>
<!--因为filter比bean先加载,也就是spring会先加载filter指定的类到container中,
这样filter中注入的spring bean就为null了。
解决办法:先filter中加入DelegatingFilterProxy类,"targetFilterLifecycle"指明作用于filter的所有生命周期。
原理是,DelegatingFilterProxy类是一个代理类,所有的请求都会首先发到这个filter代理,
然后再按照"filter-name"委派到spring中的这个bean。-->
<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>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <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> <!-- Shiro Filter --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <!--登录url--> <!-- 拦截到,跳转到的地址,通过此地址去认证 --> <property name="loginUrl" value="/admin/login.do" /> <!--登录成功跳转url--> <property name="successUrl" value="/login/main.htm"/> <!--未授权url,无权限页--> <property name="unauthorizedUrl" value="/login/unauthorize.htm"/> <property name="filters"> <map> <entry key="authc" value-ref="oAuth2AuthenticationFilter"/> <entry key="logout" value-ref="logoutFilter"/> </map> </property> <!--过滤规则--> <property name="filterChainDefinitions"> <value> /checkpreload.htm = anon /agent.ateye = anon /agent.jveye = anon / = authc /login/callback.htm = anon /login/login.htm = anon /login/checkLogin.htm = anon /login/unauthorized.htm = anon /login/changeSeller.htm = authc /login/logout = logout /login/api/** = anon /login/** = anon /** = authc </value> </property> </bean> <!--调用org.apache.shiro.SecurityUtils.setSecurityManager方法入参为securityManager,方便日后使用SecurityUtils工具类查询登录相关信息--> <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager" /> <property name="arguments" ref="securityManager" /> </bean> <!-- 配置使用自定义认证器 --> <bean id="oauthRealm" class="com.xs.personal.loginServer.common.OauthRealm"/> <!-- 核心 securityManager定义 安全管理器--> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!--注入realm--> <property name="realm" ref="oauthRealm" /> <!-- 注入session管理器 --> <property name="sessionManager" ref="defaultWebSessionManager"/> <!-- 注入缓存管理器 使用redis缓存 --> <property name="cacheManager" ref="redisCacheManager"/> </bean> <!-- 缓存管理器 使用redis缓存 --> <bean id="redisCacheManager" class="com.alitrip.hotel.helogin.client.shiro.ShiroRedisCacheManager"> </bean> <!--会话管理--> <!--传统结构项目中,shiro从cookie中读取sessionId以此来维持会话,在前后端分离的项目中 (也可在移动APP项目使用), 我们选择在ajax的请求头中传递sessionId,因此需要重 写shiro获取sessionId的方式。 自定义ShiroSessionManager类继承 DefaultWebSessionManager类,重写getSessionId方法,--> <bean id="defaultWebSessionManager" class="org.apache.shiro.web.session.mgt.ShiroSessionManager"> <!-- session的失效时长,单位毫秒 --> <property name="globalSessionTimeout" value="172800000"/> <!-- 设置session的失效扫描间隔,单位为毫秒 --> <property name="sessionValidationInterval" value="172800000"/> <!-- 删除失效的session --> <property name="deleteInvalidSessions" value="true"/> <!-- 需要让此session可以使用该定时调度器进行检测,注意持久化环境下会非常耗内存 --> <property name="sessionValidationSchedulerEnabled" value="false"/> <!--sheduler--> <!-- 定义要使用的无效Session定时调度器 --> <property name="sessionValidationScheduler" ref="sessionValidationScheduler"/> <!-- 定义Session持久化 --> <property name="sessionDAO" ref="sessionDAO"/> <!-- 所有的session一定要将id设置到Cookie之中,需要提供有Cookie的操作模版 --> <property name="sessionIdCookie" ref="sessionIdCookie"/> <!-- 定义sessionIdCookie模版可以进行操作的启用 --> <property name="sessionIdCookieEnabled" value="true"/> </bean> <!-- 配置Session DAO的操作处理 --> <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO"> <!-- 设置session缓存的名字,这个名字可以任意 --> <property name="activeSessionsCacheName" value="shiro-activeSessionCache"/> <!-- 定义该Session DAO操作中所使用的ID生成器 --> <property name="sessionIdGenerator" ref="sessionIdGenerator"/> </bean> <!-- 定义Session ID生成管理器 --> <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator" /> <!-- 会话Cookie模板 session保存到cookie 关闭浏览器下次可以直接登录认证 2天过期--> <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> <!-- 设置Cookie名字, 默认为: JSESSIONID 会与SERVLET容器名冲突, 如JETTY, TOMCAT ,重命名--> <constructor-arg value="sid"/> <!--如果您在cookie中设置了HttpOnly属性,那么通过js脚本将无法读取到cookie信息,这样能有效的防止XSS攻击--> <property name="httpOnly" value="true"/> <!-- 定义Cookie的过期时间,单位为秒,如果设置为-1表示浏览器关闭,则Cookie消失 --> <property name="maxAge" value="172800000"/> </bean> <!--默认一分钟验证session是否过期(timestamp 和 timeout)2天过期--> <bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler"> <!-- 随后还需要定义有一个会话管理器的程序类的引用 --> <property name="sessionManager" ref="defaultWebSessionManager"/> <!-- 设置session的失效扫描间隔,单位为毫秒 --> <property name="sessionValidationInterval" value="172800000"/> </bean> <bean id="oAuth2AuthenticationFilter" class="com.。。。。"> </bean> <bean id="logoutFilter" class="com.。。。。"/> </beans>
property name="filters"可以配置我们自定义的filter
shiron自带的filter如下我们可以继承这些filter来实现自己需要的一些功能,这些filter也就是filterChainDefinitions过滤规则所执行的filter我们可以根据过滤条件选择我们需要继承的filter
Filter Name | Class | 含义 |
---|---|---|
anon | org.apache.shiro.web.filter.authc.AnonymousFilter | 没有参数,表示可以匿名使用。 |
authc | org.apache.shiro.web.filter.authc.FormAuthenticationFilter | 表示需要认证(登录)才能使用,没有参数 |
authcBasic | org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter | 没有参数表示httpBasic认证 |
logout | org.apache.shiro.web.filter.authc.LogoutFilter | |
noSessionCreation | org.apache.shiro.web.filter.session.NoSessionCreationFilter | |
perms | org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter | 参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms[“user:add:,user:modify:”],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。 |
port | org.apache.shiro.web.filter.authz.PortFilter | 当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?后面的参数。 |
rest | org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter | 根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。 |
roles | org.apache.shiro.web.filter.authz.RolesAuthorizationFilter | 参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles[“admin,guest”],每个参数通过才算通过,相当于hasAllRoles()方法。 |
ssl | org.apache.shiro.web.filter.authz.SslFilter | 表示安全的url请求,协议为https |
user | org.apache.shiro.web.filter.authc.UserFilter | 没有参数表示必须存在用户,当登入操作时不做检查 |
cacheManager和sessionDAO的区别:
cacheManager缓存里可以包含权限认证的缓存、用户及权限信息的缓存等,也可以做session缓存。但是sessionDAO顾名思义就是做session持久化的,只是它刚好可以使用redis来存储而已。是完全不同的两个概念,通俗来说cacheManager是用来存储用户信息的sessionDAO是用来做session缓存的。
public class ShiroRedisCacheManager implements CacheManager, Initializable, Destroyable {
private String keyPrefix = "shiro_redis_cache:";
//ShiroRedisManager 为redis工具类
private ShiroRedisManager redisManager;
@Override
public <K, V> Cache<K, V> getCache(String name) throws CacheException {
//RedisCache为Cache 实现类 实现类方法内通过redisManager进行数据操作
Cache c = new RedisCache<K, V>(redisManager, keyPrefix);
return c;
}
}
实现CacheManager接口就可以了
/** * Description:shiro框架 自定义session获取方式 *
可自定义session获取规则。这里采用ajax请求头authToken携带sessionId的方式 * **/ public
class ShiroSessionManager extends DefaultWebSessionManager {private static final String AUTHORIZATION = "authToken"; private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request"; public ShiroSessionManager(){ super(); } @Override protected Serializable getSessionId(ServletRequest request, ServletResponse response){ String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION); System.out.println("id:"+id); if(StringUtils.isEmpty(id)){ //如果没有携带id参数则按照父类的方式在cookie进行获取 System.out.println("super:"+super.getSessionId(request, response)); return super.getSessionId(request, response); }else{ //如果请求头中有 authToken 则其值为sessionId request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,REFERENCED_SESSION_ID_SOURCE); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID,id); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID,Boolean.TRUE); return id; } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
public class OauthRealm extends AuthorizingRealm { // 设置realm的名称 @Override public void setName(String name) { super.setName("customRealm"); } @Autowired private AdminUserService adminUserService; /** * 认证 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // token中包含用户输入的用户名和密码 // 第一步从token中取出用户名 String userName = (String) token.getPrincipal(); // 第二步:根据用户输入的userCode从数据库查询 TAdminUser adminUser = adminUserService.getAdminUserByUserName(userName); // 如果查询不到返回null if (adminUser == null) {// return null; } // 获取数据库中的密码 String password = adminUser.getPassword(); /** * 认证的用户,正确的密码 */ return SimpleAuthenticationInfo(adminUser, token.getCredentials(), getName()); } /** * 授权,只有成功通过doGetAuthenticationInfo方法的认证后才会执行。 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // 从 principals获取主身份信息 // 将getPrimaryPrincipal方法返回值转为真实身份类型(在上边的doGetAuthenticationInfo认证通过填充到SimpleAuthenticationInfo中身份类型), TAdminUser activeUser = (TAdminUser)SecurityUtils.getSubject().getPrincipal(); // 根据身份信息获取权限信息 // 从数据库获取到角色数据 TAdminRole roles= adminUserService.getAdminRoles(activeUser); //遍历角色下的功能位 for (RoleDO role : roles) { Set<String> permissionList = new HashSet<>(); List functionBits = role.getFunctionBitList(); for (int i = 0; i < functionBits.size(); i++) { permissionList.add(functionBits.get(i) + ""); } //将功能位列表就是最终的权限 通过在控制层@RequiresPermissions("3")注解限制权限为3才可以访问 info.addStringPermissions(permissionList); } return simpleAuthorizationInfo;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。