我对网络进行了相当多的研究,但仍无法找到解决问题的方法。到处都有人使用不符合我要求的东西。我的场景:
用户发送邮件{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"
}
答案 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会根据其身份验证过滤器使用这些凭据授予您访问权限(或不授权)。
我们完成了吗?还没。
默认的username
和password
参数称为j_username
和j_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
的类。