当前位置:网站首页>SecurityUtils. getSubject(). How to solve the problem that getprincipal() is null
SecurityUtils. getSubject(). How to solve the problem that getprincipal() is null
2022-07-01 23:37:00 【Yisu cloud】
SecurityUtils.getSubject().getPrincipal() by null How to solve the problem of
This article mainly explains “SecurityUtils.getSubject().getPrincipal() by null How to solve the problem of ”, Interested friends might as well come and have a look . The method introduced in this paper is simple and fast , Practical . Now let Xiaobian take you to learn “SecurityUtils.getSubject().getPrincipal() by null How to solve the problem of ” Well !
SecurityUtils.getSubject().getPrincipal() by null
I get getUserId(), and getUserName() Get no value .
They all pass through SecurityUtils.getSubject().getPrincipal() To get .
Repeated tests found that the reason is : stay shiroConfig Inside :
The method , Be careful ( Is the interface name ) Written as anon, Not verified , namely :
filterMap.put("/druid/**", “anon”);
This kind of writing is for postman The test is not blocked .
terms of settlement
Accessing the backend from the page does not require anon 了 .
anon
Is not to intercept ,authc Is to take the custom interceptionoauth3
It's built-in interception
It is amended as follows :
filterMap.put("/druid/**", “authc”);
shiro SecurityUtils.getSubject() In-depth analysis of
1. in general ,SecurityUtils.getSubject() Is to create one for each request Subject, And save to ThreadContext Of resources(ThreadLocal<Map<Object, Object>>) variable , That's one http Request one subject, And bind to the current process .
The problem is coming. :.subject.login() After successful login authentication , How do you know which user is the next request ?
Friendship tips : The only thing you can read in this article is to analyze this question , If you already understand, you don't have to look down .
First, give the internal principle :1 A request 1 individual Subject principle : because ShiroFilterFactoryBean The essence is a AbstractShiroFilter filter , So every request will be executed doFilterInternal Inside createSubject Method .
guess : because subject Is bound to the current thread , This definitely requires an intermediary to store state
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 Upon successful landing , How do you know which user is the next request ?
After source code analysis , The core implementation is as follows DefaultSecurityManager Class :
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; }
Every request will be reset Session and Principals, You can probably guess : If it is web engineering , Directly from web Container acquisition httpSession, And then from httpSession obtain Principals, The essence is from cookie Get user information , Then set it every time Principal, In this way, we can know which user made the request , And only get whether this user has been successfully authenticated ,-- The essence : Browser dependent cookie To maintain the session Of
Expand , If not web Container of app, How to realize stateless session
1. The general practice will be in header With a token, Or in parameters , Backstage according to this token To verify the identity of this user , But at this point ,servlet Medium session Cannot save , We are at this time , You need to create your own session , The common practice is to rewrite session And request The interface of , Then the filter is replacing it with its own request, So what you get session It's my own session, And then according to token To create and maintain sessions
2.shiro Realization :
rewrite shiro Of sessionManage
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> * Time 2017/11/8 15:55 * explain ... */ public class StatelessSessionManager extends DefaultWebSessionManager { /** * This is the server to return to the client , */ public final static String TOKEN_NAME = "TOKEN"; /** * This is what the client requests to bring to the server 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(); } // This code hasn't been checked yet , But this is the code owned by its parent class , After rewriting, I copied it ... Start 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()); // This code hasn't been checked yet , But this is the code owned by its parent class , After rewriting, I copied it ... end return token; }} @RequestMapping("/") public void login(@RequestParam("code")String code, HttpServletRequest request){ Map<String,Object> data = new HashMap<>(); if(SecurityUtils.getSubject().isAuthenticated()){ // The code here indicates that the login has been successful , So naturally, there is no need to re authenticate , Directly from rquest Just take it out , data.put(StatelessSessionManager.HEADER_TOKEN_NAME,getServerToken()); data.put(BIND,ShiroKit.getUser().getTel() != null); response(data); } LOG.info(" Authorization code is :" + code); AuthorizationService authorizationService = authorizationFactory.getAuthorizationService(Constant.clientType); UserDetail authorization = authorizationService.authorization(code); Oauth3UserDetail userDetail = (Oauth3UserDetail) 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); // The code here , It must be put in login The implementation of , because login after , Will create session, Will get the latest token Slightly 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> * Time 2017/11/8 15:40 * explain ... */ @Configuration public class ShiroConfiguration { @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){ return new LifecycleBeanPostProcessor(); } /** * Inject one here 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; } }
Here we are , I'm sure you're right “SecurityUtils.getSubject().getPrincipal() by null How to solve the problem of ” Have a deeper understanding of , You might as well put it into practice ! This is the Yisu cloud website , For more relevant contents, you can enter the relevant channels for inquiry , Pay attention to our , Continue to learn !
边栏推荐
- SWT/ANR问题--SWT 导致 kernel fuse deadlock
- Zero foundation tutorial of Internet of things development
- mysql:insert ignore、insert和replace区别
- SWT/ANR问题--SWT 导致 low memory killer(LMK)
- Leetcode(34)——在排序数组中查找元素的第一个和最后一个位置
- const // It is a const object...class nullptr_t
- Notes to problems - file /usr/share/mysql/charsets/readme from install of mysql-server-5.1.73-1 glibc23.x86_ 64 c
- 字典、哈希表、数组的概念
- Experience of practical learning of Silicon Valley products
- PostgreSQL source code (57) why is the performance gap so large in hot update?
猜你喜欢
Overview of edge calculation
Material Design组件 - 使用BottomSheet展现扩展内容(一)
有没有一段代码,让你为人类的智慧所折服
Redis master-slave synchronization
Deep learning | three concepts: epoch, batch, iteration
Yunxin small class | common cognitive misunderstandings in IM and audio and video
Chapter 6 data flow modeling
The best smart home open source system in 2022: introduction to Alexa, home assistant and homekit ecosystem
De PIP. Interne. CLI. Main Import main modulenotfounderror: No module named 'PIP'
2022 examination questions and online simulation examination for safety management personnel of hazardous chemical business units
随机推荐
Daily three questions 6.30 (2)
Write some suggestions to current and future doctoral students to sort out and share
Matplotlib常用设置
[swoole Series 1] what will you learn in the world of swoole?
物联网技术应用属于什么专业分类
y53.第三章 Kubernetes从入门到精通 -- ingress(二六)
Chapter 6 data flow modeling
【无标题】
华为HMS Core携手超图为三维GIS注入新动能
MySQL Replication中并行复制怎么实现
Who do you want to know when opening a stock account? Is it safe to open an account online?
Reproduction process and problems of analog transformer (ICLR 2022 Spotlight)
Notes on problems - /usr/bin/perl is needed by mysql-server-5.1.73-1 glibc23.x86_ sixty-four
De PIP. Interne. CLI. Main Import main modulenotfounderror: No module named 'PIP'
安全协议重点
每日三题 6.28
PostgreSQL source code (57) why is the performance gap so large in hot update?
力扣今日题-241. 为运算表达式设计优先级
Practical application and extension of plain framework
在长城证券上买基金安全吗?