SpringBoot系列: SpringBoot Web项目中使用Shiro 之二

==================================
Shiro 的加深理解:
==================================
1. Shiro 和 Spring 系组件的对标, Shiro = Spring Security + Spring Session. 就是说 Shiro 不仅仅是一个安全框架, 同时也是一个 Session 管理框架. 其实这也是很自然的事情, Shiro 会拦截所有的 web 请求, 通过 SecurityManager 进行安全审核, 接着 SecurityManager 又委托 SessionManager 进行 session 管理. 当然 Shiro 并没有强制我们一定要使用它来进行 Session 管理.
2. Shiro Session 管理: 就是和普通的 web session 管理一样, 管理 session 属性, 默认是保存在内存中, 也可以保存在分布式的存储中, 比如 redis.
3. Shiro cache 的内容是: 仅仅缓存 Realm 的认证和鉴权相关的数据, 不会包含 Session 属性数据.

默认情况下, Web 服务器端的 Session 都是保存在内存中, 如果服务重启升级, Session 就丢了, 如果是集群部署, session 保存在单机内存中也不太合适, 这时就需要引入集中 session 存储了, 现在比较流行使用 redis 存储.

 

==================================
Shiro 内置的 Session Manager
==================================
Session Manager, Shiro 使用 session 来管理 subject 的生命周期, 包括: 创建/停止/过期 等等, shiro 内置了三个实现:
1. DefaultSessionManager
    DefaultSecurityManager 使用的默认 Session Manager, 用于 JavaSE 环境.
2. ServletContainerSessionManager
    DefaultWebSecurityManager 默认使用的 Session Manager, 使用 Servlet 容器服务器的会话. 如果运行在 Tomcat 中, HttpSession 接口的实现类是 org.apache.catalina.session.StandardSessionFacade. 用于 Web 环境.
3. DefaultWebSessionManager
    可以替代 ServletContainerSessionManager, 自定义 Session 的管理策略, 甚至包括自定义持久化方案, Shiro 提供了一个默认的 MemorySessionDAO 持久化 DAO 实现, 也有 redis 持久化方案, 比如这个开源项目, http://alexxiyang.github.io/shiro-redis/

 


下面是启用 DefaultWebSessionManager 的简单示例, 采用了默认的 MemorySessionDAO 来持久化 (应该是基于并发 HashMap 类).

@Bean
public DefaultWebSessionManager sessionManager() {
    DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
    sessionManager.setGlobalSessionTimeout(1800000l);
    sessionManager.setDeleteInvalidSessions(true);
    sessionManager.setSessionValidationSchedulerEnabled(true);
    sessionManager.setSessionIdCookieEnabled(true);
    SimpleCookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);  //cookie 名为: JSESSIONID
    cookie.setHttpOnly(true);
    cookie.setMaxAge(1800000);
    sessionManager.setSessionIdCookie(cookie);
    return sessionManager;
}

==================================
使用 Shiro 框架后几个常用对象的具体实现类
==================================
Shiro 不管是采用 ServletContainerSessionManager 还是 DefaultWebSessionManager, 使用 Shiro 框架后, 视图方法注入的 HttpServletRequest 参数都会被替换为 shiro 的 ShiroHttpServletRequest 实现类.

另外需要注意, Shiro subject.getSession() 返回的 session 对象, 实现的接口是 org.apache.shiro.session.Session, 这个接口和标准 HttpSession 接口没有父子关系, 不能简单强转, Shiro 内部有相互转换的代码.
1. subject.getSession() 返回值的具体类: org.apache.shiro.subject.support.DelegatingSubject$StoppingAwareProxiedSession
2. Controller 视图方法注入的 HttpSession 接口参数, 具体实现类是 org.apache.catalina.session.StandardSessionFacade
3. Controller 视图方法中, 通过 request.getSession() 方法, 返回值的类型和 视图方法注入的 HttpSession 参数类型一致.

详情对象的类型如下:

subject.getSession()     : org.apache.shiro.subject.support.DelegatingSubject$StoppingAwareProxiedSession
httpSession              : org.apache.catalina.session.StandardSessionFacade
httpRequest.getSession() : org.apache.catalina.session.StandardSessionFacade
httpRequest              : org.apache.shiro.web.servlet.ShiroHttpServletRequest
httpResponse             : org.apache.catalina.connector.ResponseFacade

 

下图是几个Session类之间的关系:

 

==================================
Shiro Session 和标准 HttpSession 的互操作性
==================================
虽然 org.apache.shiro.session.Session 接口和标准的 javax.servlet.http.HttpSession 接口没有父子关系, 它们不能简单强转, 但是对于 Session 属性管理是相通的, 经我测试, Shiro 不管是采用 ServletContainerSessionManager 作为 session 管理器, 还是采用 DefaultWebSessionManager, 两套 session 对象的属性读写是相通的.

这为我们提供了极大的便利, 也就是说我们既可以使用标准的 HttpSession 对象来读写 session 属性, 也可以使用 Shiro 的 Session 对象来读写.

 

// HttpSession 主动写 Session 属性示例
@RequestMapping("/springWrite")
@ResponseBody
public String springWrite(HttpServletRequest httpRequest, HttpSession httpSession){
    httpSession.setAttribute("Spring_Attr", "Spring_Attr_Value");
    System.out.println(httpSession.getAttribute("Spring_Attr"));

    System.out.println("=======");
    Session session = SecurityUtils.getSubject()
            .getSession();
    System.out.println(session.getAttribute("Spring_Attr"));
    return "springWrite";
}

// Shiro Session 主动写 Session 属性示例
@RequestMapping("/shiroWrite")
@ResponseBody
public String shiroWrite(HttpServletRequest httpRequest, HttpSession httpSession){
    Session session = SecurityUtils.getSubject()
            .getSession();
    session.setAttribute("Shiro_Attr", "Shiro_Attr_value");
    System.out.println(session.getAttribute("Shiro_Attr"));

    System.out.println("=======");
    System.out.println(httpSession.getAttribute("Shiro_Attr"));
    return "shiroWrite";
}

 

下面是 shiro session 支持的方法:

    Session session = subject.getSession(); 
    session.getId()
    session.getTimeout())
    session.getStartTimestamp();  
    session.getLastAccessTime();
    session.setAttribute("key", "123"); 
    session.removeAttribute("key");  
    session.touch();  
    session.stop();  

在 J2SE 项目中, 使用 Subject.logout() 会自动调用 Session.stop() 方法来销毁会话, 如果在 web 中, 调用 javax.servlet.http.HttpSession.invalidate() 也会自动调用 Session.stop() 方法进行销毁 Shiro 的会话.  

 

==================================
到底要不要让 Shiro 全面接管 Session 管理?
==================================
前面已经提到: Shiro = Spring Security + Spring Session, 在安全管理这块, 我觉得 Shiro 比 Spring Security 强太多了, Spring Security 搞的太复杂了. 现在的问题是, 要不要让 Shiro 全面接管 Session 管理?

推荐方案是 ServletContainerSessionManager + Spring Session, 也就是说 Session 管理还是交给 Spring Session, Session 属性操作还是使用标准的HttpSession注入对象来完成,  不太推荐使用 DefaultWebSessionManager 使用持久化 Session 信息.

说明如下:
1. 推荐 ServletContainerSessionManager 而不是 DefaultWebSessionManager, 这样就能更好利用 Spring Session 生态. 如果使用 DefaultWebSessionManager, 开箱即用的 SessionDAO 只有 MemorySessionDAO 和 开源的 shiro-redis (地址 http://alexxiyang.github.io/) , 生态要比 Spring Session 差.
比如, 我们我们
2. 有克制地使用 SecurityUtils.getSubject().getSession(), 虽然 Shiro Session 和标准 HttpSession 具有很好的互操作性, 但在视图方法中, 还是推荐使用 HttpSession 参数注入对象, 在 Shiro realm 实现类中, 如果要读写 session, 直接用 SecurityUtils.getSubject().getSession() 非常方便.



==================================
参考
==================================
使用 redis 进行基于 shiro 的 session 集群共享
http://www.cnblogs.com/sunshine-2015/p/5686750.html
shiro 实现 APP、web 统一登录认证和权限管理
http://www.cnblogs.com/sunshine-2015/p/5515429.html

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当然可以,以下是一个使用 Shiro 的 Spring Boot 项目的示例: 首先,您需要在 pom.xml 文件添加以下依赖项: ```xml <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-web-starter</artifactId> <version>1.4.2</version> </dependency> ``` 接下来,您需要在 application.properties 文件配置 Shiro: ```properties # Shiro 配置 shiro: # 登录 URL loginUrl: /login # 登录成功后跳转的 URL successUrl: /index # 未授权 URL unauthorizedUrl: /unauthorized # Shiro 过滤器链配置 filterChainDefinitions: /static/**=anon\n/login=anon\n/logout=logout\n/**=authc ``` 然后,您需要创建一个 Shiro 配置类: ```java @Configuration public class ShiroConfig { /** * 创建 Shiro 过滤器工厂 */ @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); shiroFilterFactoryBean.setLoginUrl("/login"); shiroFilterFactoryBean.setSuccessUrl("/index"); shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized"); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/static/**", "anon"); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/logout", "logout"); filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } /** * 创建 SecurityManager */ @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(realm()); return securityManager; } /** * 创建 Realm */ @Bean public Realm realm() { return new MyRealm(); } /** * 开启 Shiro 注解支持 */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager); return advisor; } /** * 开启 Shiro AOP 支持 */ @Bean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator(); creator.setProxyTargetClass(true); return creator; } } ``` 最后,您需要创建一个 Realm 类来处理身份验证和授权: ```java public class MyRealm extends AuthorizingRealm { /** * 处理授权 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); authorizationInfo.addRole("admin"); authorizationInfo.addStringPermission("user:list"); return authorizationInfo; } /** * 处理身份验证 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String) token.getPrincipal(); String password = new String((char[]) token.getCredentials()); if (!"admin".equals(username)) { throw new UnknownAccountException("用户名或密码错误"); } if (!"123456".equals(password)) { throw new IncorrectCredentialsException("用户名或密码错误"); } return new SimpleAuthenticationInfo(username, password, getName()); } } ``` 这就是一个简单的使用 Shiro 的 Spring Boot 项目的示例。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值