如何骗过Spring Security——直接添加Authentication对象

本文介绍如何在不使用标准Web登录流程的情况下,通过直接添加Authentication对象到SpringSecurity中,实现自定义的身份验证。探讨了SecurityContextHolder、AuthenticationManager及核心组件的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如何骗过Spring Security——直接添加Authentication对象

1.需求

在最近的项目中,出现了这样的需求。我需要在后台使用spring security,但是android端显然不能使用像web端登录那样的处理方式,所以如何"骗过"spring security直接在它的认证流程中插入我自己的对象,这成为了我急切的问题。

2.Spring Security的核心组件

SecurityContextHolder

SecurityContextHolder用于获取当前用户的信息,SecurityContextHolder默认使用ThreadLocal来存储认证信息,这是一种与线程绑定的策略。只要在同一个线程中进行,即使不在各个方法之间以参数的形式传递,各个方法也能通过SecurityContextHolder工具获取到安全上下文。

获取当前的用户信息

Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}

Authentication

源码

public interface Authentication extends Principal, Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();

    Object getCredentials();//密码信息

    Object getDetails();//细节信息,记录了ip地址和sessionId的值

    Object getPrincipal();//大部分情况下返回UserDetais的实现类

    boolean isAuthenticated();//是否认证

    void setAuthenticated(boolean var1) throws IllegalArgumentException;
}

AuthenticationManager

正如他的名字一样,认证的管理者。他的作用不是直接亲自上场进行认证,而是委派其他有能力的AuthenticationProvider(实际上是实现此接口的实现类进行认证)进行认证,而AuthenticationProvider又由ProviderManager提供。在实际的需求中,我们登陆的方式不同,认证的方式就会不同,所以设计成这样后,如果我们需要自己定义登录方式,则只需要提供相应的AuthenticationProvider就可以了。

DaoAuthenticationProvider

提交的用户名和密码,被封装成了UsernamePasswordAuthenticationToken,而根据用户名加载用户的任务则是交给了UserDetailsService,在DaoAuthenticationProvider中,对应的方法便是retrieveUser,虽然有两个参数,但是retrieveUser只有第一个参数起主要作用,返回一个UserDetails。还需要完成UsernamePasswordAuthenticationToken和UserDetails密码的比对,这便是交给additionalAuthenticationChecks方法完成的,如果这个void方法没有抛异常,则认为比对成功。比对密码的过程,用到了PasswordEncoder和SaltSource。

UserDetails与UserDetailsService

UserDetais源码

public interface UserDetails extends Serializable {
   Collection<? extends GrantedAuthority> getAuthorities();
   String getPassword();
   String getUsername();
   boolean isAccountNonExpired();
   boolean isAccountNonLocked();
   boolean isCredentialsNonExpired();
   boolean isEnabled();
}

UserDetails是与UsernamePasswordAuthenticationToken比对的对象,由UserDetailsService获得。UserDetailsService是我们组装UserDetails的地方,我们也可以实现这个接口来完成自定义的组装。

3.Spring Security身份认证流程

  1. 用户名和密码被过滤器获取到,封装成Authentication,通常情况下是UsernamePasswordAuthenticationToken这个实现类。
  2. AuthenticationManager 身份管理器负责验证这个Authentication
  3. 认证成功后,AuthenticationManager身份管理器返回一个被填充满了信息的(包括上面提到的权限信息,身份信息,细节信息,但密码通常会被移除)Authentication实例。
  4. SecurityContextHolder安全上下文容器将第3步填充了信息的Authentication,通过SecurityContextHolder.getContext().setAuthentication(…)方法,设置到其中。

4.将自己需要的Authentication对象放入Spring Security中

@RequestMapping("/doLogin")
    public int doLogin(@RequestParam("userName") String userName,@RequestParam("password") String password,HttpServletRequest request){
        User user = simpleUserService.getUserByName(userName);
		
        //如果用户不存在则抛出异常
        if(user==null){
            throw new UsernameNotFoundException("没有当前用户");
        }

        else {
            //如果用户存在且用户的密码相同,则在SecurityContextHolder.getContext().setAuthentication()放入authentication
            if(user.getPassword().equals(password)){
                UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(userName, password, AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRole()));
                authRequest.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authRequest);
                return 1;
            }
        }
        

        return 0;
    }
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值