Spring安全性 - 从控制器

时间:2017-02-13 14:04:25

标签: spring spring-mvc spring-boot spring-security

我对网络进行了相当多的研究,但仍无法找到解决问题的方法。到处都有人使用不符合我要求的东西。我的场景:

用户发送邮件{email:“admin”,密码:“admin”}然后有登录控制器检查输入是否正确,并在标题/正文中返回该标记后。

我坚持使用从控制器传递值到Spring身份验证过程的部分。

以下是我的配置:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


    @Autowired
    private TokenAuthenticationProvider tokenAuthenticationProvider;


    @Autowired
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(this.tokenAuthenticationProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/login").permitAll()
                .anyRequest().authenticated().and()
                .csrf().disable();
    }
}

这是我的控制器:

@RestController
public class LoginController {

    @Autowired
    private AuthenticationManager authenticationManager;

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public ResponseEntity<?> login(@RequestBody LoginCredentials payload){

        Authentication authentication = this.authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        payload.getUsername(),
                        payload.getPassword()
                )
        );

        SecurityContextHolder.getContext().setAuthentication(authentication);
        return ResponseEntity.ok("OK");
    }
}

这是我的身份验证提供程序

@Component
public class TokenAuthenticationProvider implements AuthenticationProvider {


    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        if(authentication.getName().equals("admin")  && authentication.getCredentials().equals("admin")) {
            List<GrantedAuthority> grantedAuths = new ArrayList<>();
            grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
            grantedAuths.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
            return new UsernamePasswordAuthenticationToken(authentication.getName(), authentication.getCredentials(), grantedAuths);
        } else {
            return null;
        }
    }

    @Override
    public boolean supports(Class<? extends Object> authentication) {
        return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
    }
}

我正在使用凭据进行发布请求,这就是我得到的答案:

{
  "timestamp": 1486995388844,
  "status": 403,
  "error": "Forbidden",
  "exception": "org.springframework.security.authentication.ProviderNotFoundException",
  "message": "Access Denied",
  "path": "/login"
}

1 个答案:

答案 0 :(得分:2)

有趣的是,您选择创建一个新的控制器来处理登录,但实际上您正在复制Spring Security为您所做的工作。考虑一下:您向自定义控制器(在某个端点)发送HTTP POST请求,该请求将通过调用身份验证提供程序来检查要验证的用户的凭据 - 这已经是Spring Security模块的机制工作(您需要对某个端点执行POST,安全容器将调用已定义的身份验证提供程序,并决定是否应授予该用户访问权限。)

我相信你不需要那个控制器:你只需要对你的安全容器进行一些额外的配置,所以让我们开始吧!

BTW:即使Spring配置主要用Java编写,Spring Security配置还不支持完整的Java,但大部分仍需要XML。

您可以将Spring Security配置为在命中某个端点时使用该HTTP POST请求初始化身份验证过程。在XML中,这可以通过以下方式完成:

<http>
    <form-login login-processing-url="/perform_login"/>

    ...(other configuration)
</http>

虽然我试图在documentation中找到它,但我无法找到等效的Java配置。我们可以在formLogin()对象上调用HttpSecurity方法,如下所示:

protected void configure(HttpSecurity http) throws Exception {
    http
    .authorizeRequests()
        .anyRequest().authenticated()
        .and()
    .formLogin()
        .loginPage("/login")
        .permitAll();
 } 

但似乎没有Java方法来配置登录处理URL。如果需要登录,loginPage()方法只会指定发送用户的URL(这不是我们要查找的内容)。

如果您没有配置登录处理URL,安全容器将只使用/j_spring_security_check的默认值。

现在我们可以将我们的身份验证凭据发布到/perform_login,Spring Security会根据其身份验证过滤器使用这些凭据授予您访问权限(或不授权)。

我们完成了吗?还没。 默认的usernamepassword参数称为j_usernamej_password,因此您必须将这些参数发送到/perform_login以获得所需的行为。幸运的是,我们可以配置这两个的名称(再次使用XML格式 - 抱歉):

<http>
    <form-login login-processing-url="/perform_login"/>
                username-parameter="email"
                password-parameter="password"

    ...(other configuration)
</http>

现在我们定了!通过发出带有两个参数的HTTP POST(如果你想要CSRF令牌,可以更多) - 即电子邮件密码 / perform_login ,我们可以正确验证(我们没有像你那样创建另一个名为LoginCredentials的类。