使用 Spring Security 4 以编程方式对用户进行身份验证

Posted

技术标签:

【中文标题】使用 Spring Security 4 以编程方式对用户进行身份验证【英文标题】:Programatically authenticate user with Spring Security 4 【发布时间】:2016-05-11 09:37:12 【问题描述】:

我使用 inMemoryAuthentication 并且需要在访问特定端点时以编程方式对用户进行身份验证(出于测试原因)。因此我使用以下基于Programmatically login in a user using spring security的方法:

private void login(Role role) 
    logger.warn("Login User with role: " + role.toString());
    Authentication auth =
        new UsernamePasswordAuthenticationToken("user", "password", getAuthorities(role));
    SecurityContext securityContext = SecurityContextHolder.getContext();
    securityContext.setAuthentication(auth);


private Collection<GrantedAuthority> getAuthorities(Role role) 
    Collection<GrantedAuthority> grantedAuthorities = new ArrayList<>();
    GrantedAuthority grantedAuthority = (GrantedAuthority) () -> role.toString();
    grantedAuthorities.add(grantedAuthority);
    return grantedAuthorities;

securityContext 设置正确(getAuthentication 按预期返回用户),但是,JSESSIONID cookie 未设置。

【问题讨论】:

【参考方案1】:

我可以解决我的问题,但这并不容易,我不确定是否有更好的解决方案。

我创建了一个过滤器、一个自定义 authenticationProvider 和一个自定义 authenticationToken

public class E2EAuthenticationFilter extends GenericFilterBean 
    private final Logger logger = LoggerFactory.getLogger(E2EAuthenticationFilter.class);

    private AuthenticationManager authenticationManager;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException 
        Role role = null;
        String username = null;
        String password = null;
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        if(isLoginRequest(request.getRequestURI()))
            String roleName = getRoleName(request.getRequestURI());
            switch(roleName.toUpperCase())
                case "ADMIN":
                    role = Role.ADMIN;
                    username = "admin";
                    password = "admin";
                    break;
                case "REPORTER":
                    role = Role.REPORTER;
                    username = "reporter";
                    password = "reporter";
                    break;
                default:
                    break;
            

            authenticateUser(username, password, role);
        

        chain.doFilter(request, response);
    

    public AuthenticationManager getAuthenticationManager() 
        return authenticationManager;
    

    public void setAuthenticationManager(AuthenticationManager authenticationManager) 
        this.authenticationManager = authenticationManager;
    

    private Collection<GrantedAuthority> getAuthorities(Role role) 
        Collection<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        GrantedAuthority grantedAuthority = (GrantedAuthority) () -> "ROLE_"+role.toString();
        grantedAuthorities.add(grantedAuthority);
        return grantedAuthorities;
    

    private String getRoleName(String uri)
        String[] requestUriParts = uri.split("/");
        return requestUriParts[requestUriParts.length-1];
    

    private void authenticateUser(String username, String password, Role role)
        E2EAuthenticationToken token = new E2EAuthenticationToken(username, password, getAuthorities(role));
        Authentication authResult = authenticationManager.authenticate(token);
        SecurityContextHolder.getContext().setAuthentication(authResult);
    

    private boolean isLoginRequest(String uri) 
        return uri.matches("\\/e2e\\/auth\\/login\\/(\\w)+");
    

authProvider 类:

@Component
public class E2EAuthenticationProvider implements AuthenticationProvider 


    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException 
        E2EAuthenticationToken token = (E2EAuthenticationToken) authentication;
        E2EAuthenticationToken authenticated = new E2EAuthenticationToken(token.getUsername(), token.getPassword(), token.getAuthorities());

        authenticated.setAuthenticated(true);
        return  authenticated;
    

    @Override
    public boolean supports(Class<?> authentication) 
        return authentication.equals(E2EAuthenticationToken.class);
    

令牌类:

public class E2EAuthenticationToken extends AbstractAuthenticationToken 
    private String username;
    private String password;

    public E2EAuthenticationToken(String username, String password, Collection<? extends GrantedAuthority> authorities) 
        super(authorities);
        this.username = username;
    

    @Override
    public Object getCredentials() 
        return this.username;
    

    @Override
    public Object getPrincipal() 
        return this.username;
    

    public String getUsername() 
        return username;
    

    public void setUsername(String username) 
        this.username = username;
    

    public String getPassword() 
        return password;
    

    public void setPassword(String password) 
        this.password = password;
    

最后是spring配置:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class InMemorySecurityConfig extends WebSecurityConfigurerAdapter 

    @Autowired
    private E2EAuthenticationProvider e2eAuthenticationProvider;

    @Bean
    public E2EAuthenticationFilter e2eAuthenticationFilter() throws Exception 
        E2EAuthenticationFilter filter = new E2EAuthenticationFilter();
        filter.setAuthenticationManager( authenticationManager() );
        return filter;
    

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception 
        auth.authenticationProvider(e2eAuthenticationProvider);

        auth.inMemoryAuthentication()
            .withUser("admin")
            .password("admin")
            .roles(Role.ADMIN.name())
            .and()
            .withUser("reporter")
            .password("reporter")
            .roles(Role.REPORTER.name());
    

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http
            .requestMatchers().antMatchers("/api/**", "/e2e/auth/**", "/acuator/**")
            .and().addFilterBefore(e2eAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
            .authorizeRequests()
            .antMatchers("/actuator/health").permitAll()
            .antMatchers("/actuator/info").permitAll()
            .anyRequest().authenticated()
            .and().httpBasic()
            .and().csrf().disable();
    

您可能已经注意到,我将此代码用于端点(当然,它仅在 E2E 配置中可用)以登录用户进行 selenium 测试。

【讨论】:

以上是关于使用 Spring Security 4 以编程方式对用户进行身份验证的主要内容,如果未能解决你的问题,请参考以下文章

Spring security:以编程方式登录

如何使用 Spring Security 3.1 以编程方式登录用户

Spring Security 3 以编程方式登录

如何以编程方式检查某个 URL 是不是需要使用 Spring Security 进行身份验证?

如何使用 DaoAuthenticationProvider 以编程方式使用 Spring Security 对用户进行身份验证

作为管理员,如何使用 Grails Spring Security 以编程方式更改用户密码?