正常情况下,我们登录都是用账号密码登录,spring security的登录逻辑是在UsernamePasswordAuthenticationFilter里,这个类继承了AbstractAuthenticationProcessingFilter,我们如果想实现自己的登陆判断业务逻辑,可以继承AbstractAuthenticationProcessingFilter来实现,然后 http.addFilterAt(new MyUsernamePasswordFilter(), UsernamePasswordAuthenticationFilter.class) 这样就替换成自己的filter了。
UsernamePasswordAuthenticationFilter里的这段源码会把登录成功后的用户token信息放入上下文。
1 public Authentication attemptAuthentication(HttpServletRequest request, 2 HttpServletResponse response) throws AuthenticationException { 3 if (postOnly && !request.getMethod().equals("POST")) { 4 throw new AuthenticationServiceException( 5 "Authentication method not supported: " + request.getMethod()); 6 } 7 8 String username = obtainUsername(request); 9 String password = obtainPassword(request); 10 11 if (username == null) { 12 username = ""; 13 } 14 15 if (password == null) { 16 password = ""; 17 } 18 19 username = username.trim(); 20 21 UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken( 22 username, password); 23 24 // Allow subclasses to set the "details" property 25 setDetails(request, authRequest); 26 27 return this.getAuthenticationManager().authenticate(authRequest); 28 }
如果我不用上面的方法,自己在某个方法里需要实现登录并且session共享的功能,可以这样写。
1 UserInfo userInfo = (UserInfo) userDetailsService.loadUserByUsername(username); 2 UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userInfo, password, userInfo.getAuthorities()); 3 4 // 设置authentication中details 5 authentication.setDetails(new WebAuthenticationDetails(request)); 6 7 SecurityContextHolder.getContext().setAuthentication(authentication); 8 9 // 在session中存放security context,方便同一个session中控制用户的其他操作 10 HttpSession session = request.getSession(true); 11 // 这一步很关键,不能省略掉 12 session.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext());
SecurityContextHolder是SpringSecurity最基本的组件了,是用来存放SecurityContext的对象,默认是使用ThreadLocal实现的,这样就保证了本线程内所有的方法都可以获得SecurityContext对象。
SecurityContextHolder可以工作在以下三种模式之一:
1)MODE_THREADLOCAL (缺省工作模式)
2)MODE_GLOBAL
3) MODE_INHERITABLETHREADLOCAL
4) 修改SecurityContextHolder的工作模式有两种方法 :
a: 设置一个系统属性(system.properties) : spring.security.strategy;
SecurityContextHolder会自动从该系统属性中尝试获取被设定的工作模式
b: 调用SecurityContextHolder静态方法setStrategyName()
SecurityContextHolder存储SecurityContext的方式(默认就是mode_threadlocal)
使用SecurityContextHolder获取当前登录的用户信息
在SecurityContextHolder中保存的是当前访问者的信息。Spring Security使用一个Authentication对象来表示这个信息。一般情况下,我们都不需要创建这个对象,在登录过程中,Spring Security已经创建了该对象并帮我们放到了SecurityContextHolder中。从SecurityContextHolder中获取这个对象也是很简单的。
1 SecurityContextHolder.getContext().getAuthentication();
这样在其它接口里就可以拿到上面设置的用户信息了。