- 浏览: 561926 次
- 性别:
- 来自: 深圳
文章分类
最新评论
-
jiang2011jiang:
mybatis3源码核心类1--Configuration -
tuyf_hs:
同求 图片
zookeeper+dubbo+dubbo管理集群的简要配置[单机] -
安静听歌:
请问图片还能找的会吗?你的图片和原文的图片都挂了,,,如果有图 ...
zookeeper+dubbo+dubbo管理集群的简要配置[单机] -
ahua186186:
yngwiet 写道楼主,有一个地方不太明白,为什么要用“ge ...
ListView中getChildAt(index)的使用注意事项 -
yngwiet:
楼主,有一个地方不太明白,为什么要用“getChildAt(p ...
ListView中getChildAt(index)的使用注意事项
1.总的来说,SecurityUtils.getSubject()是每个请求创建一个Subject, 并保存到ThreadContext的resources(ThreadLocal<Map<Object, Object>>)变量中,也就是一个http请求一个subject,并绑定到当前线程。
问题来了:.subject.login()登陆认证成功后,下一次请求如何知道是那个用户的请求呢?
友情提示:本文唯一可以读一下的就是分析这个疑问,如果你已经明白就不用往下看了。
首先给出内部原理:1个请求1个Subject原理:由于ShiroFilterFactoryBean本质是个AbstractShiroFilter过滤器,所以每次请求都会执行doFilterInternal里面的createSubject方法。
猜想:因为subject是绑定到当前线程,这肯定需要一个中介存储状态
subject登陆成功后,下一次请求如何知道是那个用户的请求呢?
经过源码分析,核心实现如下DefaultSecurityManager类中:
每次请求都会重新设置Session和Principals,看到这里大概就能猜到:如果是web工程,直接从web容器获取httpSession,然后再从httpSession获取Principals,本质就是从cookie获取用户信息,然后每次都设置Principal,这样就知道是哪个用户的请求,并只得到这个用户有没有人认证成功,--本质:依赖于浏览器的cookie来维护session的
扩展,如果不是web容器的app,如何实现实现无状态的会话
1.一般的作法会在header中带有一个token,或者是在参数中,后台根据这个token来进行校验这个用户的身份,但是这个时候,servlet中的session就无法保存,我们在这个时候,就要实现自己的会话创建,普通的作法就是重写session与request的接口,然后在过滤器在把它替换成自己的request,所以得到的session也是自己的session,然后根据token来创建和维护会话
2.shiro实现:
重写shiro的sessionManage
代码实现转自:http://www.cnblogs.com/zhuxiaojie/p/7809767.html
实现代码:
问题来了:.subject.login()登陆认证成功后,下一次请求如何知道是那个用户的请求呢?
友情提示:本文唯一可以读一下的就是分析这个疑问,如果你已经明白就不用往下看了。
首先给出内部原理:1个请求1个Subject原理:由于ShiroFilterFactoryBean本质是个AbstractShiroFilter过滤器,所以每次请求都会执行doFilterInternal里面的createSubject方法。
猜想:因为subject是绑定到当前线程,这肯定需要一个中介存储状态
public static Subject getSubject() { Subject subject = ThreadContext.getSubject(); if (subject == null) { subject = (new Builder()).buildSubject(); ThreadContext.bind(subject); } return subject; }
public abstract class ThreadContext { private static final Logger log = LoggerFactory.getLogger(ThreadContext.class); public static final String SECURITY_MANAGER_KEY = ThreadContext.class.getName() + "_SECURITY_MANAGER_KEY"; public static final String SUBJECT_KEY = ThreadContext.class.getName() + "_SUBJECT_KEY"; private static final ThreadLocal<Map<Object, Object>> resources = new ThreadContext.InheritableThreadLocalMap(); protected ThreadContext() { } public static Map<Object, Object> getResources() { return (Map)(resources.get() == null ? Collections.emptyMap() : new HashMap((Map)resources.get())); } public static void setResources(Map<Object, Object> newResources) { if (!CollectionUtils.isEmpty(newResources)) { ensureResourcesInitialized(); ((Map)resources.get()).clear(); ((Map)resources.get()).putAll(newResources); } } private static Object getValue(Object key) { Map<Object, Object> perThreadResources = (Map)resources.get(); return perThreadResources != null ? perThreadResources.get(key) : null; } private static void ensureResourcesInitialized() { if (resources.get() == null) { resources.set(new HashMap()); } } public static Object get(Object key) { if (log.isTraceEnabled()) { String msg = "get() - in thread [" + Thread.currentThread().getName() + "]"; log.trace(msg); } Object value = getValue(key); if (value != null && log.isTraceEnabled()) { String msg = "Retrieved value of type [" + value.getClass().getName() + "] for key [" + key + "] bound to thread [" + Thread.currentThread().getName() + "]"; log.trace(msg); } return value; } public static void put(Object key, Object value) { if (key == null) { throw new IllegalArgumentException("key cannot be null"); } else if (value == null) { remove(key); } else { ensureResourcesInitialized(); ((Map)resources.get()).put(key, value); if (log.isTraceEnabled()) { String msg = "Bound value of type [" + value.getClass().getName() + "] for key [" + key + "] to thread [" + Thread.currentThread().getName() + "]"; log.trace(msg); } } } public static Object remove(Object key) { Map<Object, Object> perThreadResources = (Map)resources.get(); Object value = perThreadResources != null ? perThreadResources.remove(key) : null; if (value != null && log.isTraceEnabled()) { String msg = "Removed value of type [" + value.getClass().getName() + "] for key [" + key + "]from thread [" + Thread.currentThread().getName() + "]"; log.trace(msg); } return value; } public static void remove() { resources.remove(); } public static SecurityManager getSecurityManager() { return (SecurityManager)get(SECURITY_MANAGER_KEY); } public static void bind(SecurityManager securityManager) { if (securityManager != null) { put(SECURITY_MANAGER_KEY, securityManager); } } public static SecurityManager unbindSecurityManager() { return (SecurityManager)remove(SECURITY_MANAGER_KEY); } public static Subject getSubject() { return (Subject)get(SUBJECT_KEY); } public static void bind(Subject subject) { if (subject != null) { put(SUBJECT_KEY, subject); } } public static Subject unbindSubject() { return (Subject)remove(SUBJECT_KEY); } private static final class InheritableThreadLocalMap<T extends Map<Object, Object>> extends InheritableThreadLocal<Map<Object, Object>> { private InheritableThreadLocalMap() { } protected Map<Object, Object> childValue(Map<Object, Object> parentValue) { return parentValue != null ? (Map)((HashMap)parentValue).clone() : null; } } }
subject登陆成功后,下一次请求如何知道是那个用户的请求呢?
经过源码分析,核心实现如下DefaultSecurityManager类中:
public Subject createSubject(SubjectContext subjectContext) { SubjectContext context = this.copy(subjectContext); context = this.ensureSecurityManager(context); context = this.resolveSession(context); context = this.resolvePrincipals(context); Subject subject = this.doCreateSubject(context); this.save(subject); return subject; }
每次请求都会重新设置Session和Principals,看到这里大概就能猜到:如果是web工程,直接从web容器获取httpSession,然后再从httpSession获取Principals,本质就是从cookie获取用户信息,然后每次都设置Principal,这样就知道是哪个用户的请求,并只得到这个用户有没有人认证成功,--本质:依赖于浏览器的cookie来维护session的
扩展,如果不是web容器的app,如何实现实现无状态的会话
1.一般的作法会在header中带有一个token,或者是在参数中,后台根据这个token来进行校验这个用户的身份,但是这个时候,servlet中的session就无法保存,我们在这个时候,就要实现自己的会话创建,普通的作法就是重写session与request的接口,然后在过滤器在把它替换成自己的request,所以得到的session也是自己的session,然后根据token来创建和维护会话
2.shiro实现:
重写shiro的sessionManage
代码实现转自:http://www.cnblogs.com/zhuxiaojie/p/7809767.html
实现代码:
import org.apache.shiro.session.mgt.SessionKey; import org.apache.shiro.web.servlet.ShiroHttpServletRequest; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.apache.shiro.web.util.WebUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.Serializable; import java.util.UUID; /** * @author zxj<br> * 时间 2017/11/8 15:55 * 说明 ... */ public class StatelessSessionManager extends DefaultWebSessionManager { /** * 这个是服务端要返回给客户端, */ public final static String TOKEN_NAME = "TOKEN"; /** * 这个是客户端请求给服务端带的header */ public final static String HEADER_TOKEN_NAME = "token"; public final static Logger LOG = LoggerFactory.getLogger(StatelessSessionManager.class); @Override public Serializable getSessionId(SessionKey key) { Serializable sessionId = key.getSessionId(); if(sessionId == null){ HttpServletRequest request = WebUtils.getHttpRequest(key); HttpServletResponse response = WebUtils.getHttpResponse(key); sessionId = this.getSessionId(request,response); } HttpServletRequest request = WebUtils.getHttpRequest(key); request.setAttribute(TOKEN_NAME,sessionId.toString()); return sessionId; } @Override protected Serializable getSessionId(ServletRequest servletRequest, ServletResponse servletResponse) { HttpServletRequest request = (HttpServletRequest) servletRequest; String token = request.getHeader(HEADER_TOKEN_NAME); if(token == null){ token = UUID.randomUUID().toString(); } //这段代码还没有去查看其作用,但是这是其父类中所拥有的代码,重写完后我复制了过来...开始 request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, token); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); request.setAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED, isSessionIdUrlRewritingEnabled()); //这段代码还没有去查看其作用,但是这是其父类中所拥有的代码,重写完后我复制了过来...结束 return token; } }
@RequestMapping("/") public void login(@RequestParam("code")String code, HttpServletRequest request){ Map<String,Object> data = new HashMap<>(); if(SecurityUtils.getSubject().isAuthenticated()){ //这里代码着已经登陆成功,所以自然不用再次认证,直接从rquest中取出就行了, data.put(StatelessSessionManager.HEADER_TOKEN_NAME,getServerToken()); data.put(BIND,ShiroKit.getUser().getTel() != null); response(data); } LOG.info("授权码为:" + code); AuthorizationService authorizationService = authorizationFactory.getAuthorizationService(Constant.clientType); UserDetail authorization = authorizationService.authorization(code); Oauth2UserDetail userDetail = (Oauth2UserDetail) authorization; loginService.login(userDetail); User user = userService.saveUser(userDetail,Constant.clientType.toString()); ShiroKit.getSession().setAttribute(ShiroKit.USER_DETAIL_KEY,userDetail); ShiroKit.getSession().setAttribute(ShiroKit.USER_KEY,user); data.put(BIND,user.getTel() != null); //这里的代码,必须放到login之执行,因为login后,才会创建session,才会得到最新的token咯 data.put(StatelessSessionManager.HEADER_TOKEN_NAME,getServerToken()); response(data); }
import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.realm.Realm; import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; /** * @author zxj<br> * 时间 2017/11/8 15:40 * 说明 ... */ @Configuration public class ShiroConfiguration { @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){ return new LifecycleBeanPostProcessor(); } /** * 此处注入一个realm * @param realm * @return */ @Bean public SecurityManager securityManager(Realm realm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setSessionManager(new StatelessSessionManager()); securityManager.setRealm(realm); return securityManager; } @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){ ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); bean.setSecurityManager(securityManager); Map<String,String> map = new LinkedHashMap<>(); map.put("/public/**","anon"); map.put("/login/**","anon"); map.put("/**","user"); bean.setFilterChainDefinitionMap(map); return bean; } }
发表评论
-
shiro落地的设计复杂度(最后总结)
2018-06-19 17:22 577经过1周的源码研究,终于对shiro的原理有了深刻的理解,基于 ... -
shiro login成功后保存了哪些数据
2018-06-19 17:05 1467shiro login成功后 保存了Principals 和 ... -
shiro 会话原理分析
2018-06-19 12:40 15891、从哪里获取sessionid每次请求都会尝试获取ses ... -
shiro内部原理分析
2018-06-15 17:07 2404一句话总结:会话域Context一路收集principal ... -
Zookeeper入门-001 源码环境搭建
2018-03-15 11:47 9021.到github下载源码:https://github.c ... -
dubbo服务治理之路由规则研究
2018-01-31 15:50 22071.今天没太多事情,挤 ... -
@Async核心实现1 --------AsyncExecutionAspectSupport
2017-12-27 10:34 1979基本原理: 通过spring的扩展接口AbstractBea ... -
从零开始玩转JMX(1):简介和 Standard MBean
2017-08-23 15:20 0http://www.importnew.com/22299. ... -
java基础回顾
2017-08-15 11:21 0http://www.cnblogs.com/skywang1 ... -
mybatis-generator-maven-plugin 插件扩展 增加自定义方法
2017-08-10 16:50 0https://my.oschina.net/alexgaoy ... -
解决了DeferredResult请求长时间占用数据库连接的问题
2017-08-04 09:55 2354最近看了看开源项目appllo配置中心的源码,发现一个很有意思 ... -
httpclient发送webservice
2017-05-03 23:25 0http://aperise.iteye.com/blog/2 ... -
与大师面对面交流:Chris Richardson 来华布道微服务架构
2016-11-28 21:28 820http://www.daocloud.io/microser ... -
eclipse反编译
2016-11-23 20:48 0http://jingyan.baidu.com/articl ... -
spring security
2016-09-12 19:28 0http://www.importnew.com/5641.h ... -
浅析JPA中EntityManager无法remove entity的问题
2016-07-18 21:50 0http://rickqin.blog.51cto.com/1 ... -
Permission Denied(publickey) 解决
2016-07-14 19:18 27761.生成公钥和私钥放到C:\Users\itservice\. ... -
权限管理系统
2016-04-18 13:29 0http://git.oschina.net/ketayao/ ... -
Java工程师成神之路--面试必须复习的基础
2015-12-28 17:20 0http://www.open-open.com/news/v ... -
spring AOP何时添加AnnotationAwareAspectJAutoProxyCreator
2015-12-27 00:46 22341.spring AOP的原理都知道是从解析自定义标签时注册一 ...
相关推荐
在使用Shiro 1.7.1时,开发者可以根据具体需求选择合适的模块进行集成,例如,Web应用可能会主要依赖`shiro-core`、`shiro-web`和`shiro-spring`,而需要进行复杂加密操作的应用可能需要`shiro-crypto-hash`和`shiro...
Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123"); try { // 登录操作 subject.login(token); // 认证通过,返回成功信息 System....
此项目是一个简单的springboot+shiro项目,旨在重现CVE-2020-13933 shiro漏洞.zip此项目是一个简单的springboot+shiro项目,旨在重现CVE-2020-13933 shiro漏洞.zip此项目是一个简单的springboot+shiro项目,旨在重现...
ShiroConfig.java 看博客吧
例如,你可以使用 `<@shiro.hasPermission 'admin'>` 标签来检查当前用户是否拥有 "admin" 权限,如果拥有,标签内部的内容将被渲染到页面上;如果没有,这部分内容则会被忽略。这极大地简化了视图层的代码,并提高...
Subject subject = SecurityUtils.getSubject(); // 登录逻辑 } @RequiresRoles("admin") @GetMapping("/admin") public String adminPage() { return "管理员页面"; } } ``` **三、注意事项** 1. **密码...
- **Subject**:Shiro 中的主体对象,代表了当前操作的用户,可以通过 `SecurityUtils.getSubject()` 获取。 - **DefaultSecurityManager**:Shiro 提供的默认 SecurityManager 实现,可以配置 Realm 等其他组件。 -...
### Shiro教程知识点总结 #### 一、Shiro简介 - **Shiro**是一个由Apache提供的强大且易用的Java安全框架,它提供了全面的安全服务,包括认证、授权、密码管理和会话管理等功能。 - **特点**: - **简单易用**:...
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> ``` 同时,如果使用了 Spring AOP,还需要配置自动代理创建器: ```xml <bean class="org.springframework...
Apache Shiro 是一个强大且易用的 Java 安全框架,提供认证、授权、加密和会话管理功能,可简化企业级应用的安全实现。Shiro 2.alpha.0 是该框架的一个早期版本,包含了框架的核心组件和相关源代码,便于开发者理解...
一款好用的shiro检测利用工具,使用方式java -jar shiro_tool.jar https://xx.xx.xx.xx,Github有开源下载链接,在这里上传是为了赚积分下载别的工具,欢迎使用
在这个例子中,用户登录信息被封装为`UsernamePasswordToken`对象,并通过`SecurityUtils.getSubject()`方法获取当前的`Subject`实例,然后调用其`login()`方法来完成登录操作。 ```java UsernamePasswordToken ...
Apache Shiro是一个强大的Java安全框架,它为应用程序提供了身份验证(Authentication)、授权(Authorization)、会话管理(Session Management)和加密(Cryptography)等核心功能。`shiro-1.7.1.zip`是一个包含...
Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, password); try { subject.login(token); return "redirect:/index"; } catch ...
通过学习这个源代码,你将了解到如何在实际项目中配置Shiro,以及如何与SSM框架进行深度整合。同时,源代码中的注解和配置将帮助你理解每个组件的作用,进一步提升你在安全领域的技能。对于初学者而言,这是一个很好...
**标题:“shiroDemo.rar”** - 这个压缩包文件名为“shiroDemo”,暗示了它是一个关于Shiro框架的示例项目,可能包含了如何在Spring Boot应用中集成Shiro的相关代码和配置。 **描述:“springboot+shiro+jwt+redis...