我正在使用Spring安全性并且工作正常,但现在我想手动启动安全流程,做客户端更改我需要进入我的控制器的用户名和密码(表单)不会直接调用“j_spring_security_check”)
我想到了两个选项,我有一些问题:
获取参数并执行某些操作后,我会向j_spring_security_check url发送一个帖子请求。我的代码:
public void test(loginDTO loginDTO){
MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();
HttpHeaders headers = new HttpHeaders();
body.add(
"j_username",
loginDTO.getJ_username());
body.add(
"j_password",
loginDTO.getJ_password());
HttpEntity<?> httpEntity = new HttpEntity<Object>(
body, headers);
headers.add(
"Accept",
MediaType.APPLICATION_JSON_VALUE);
restTemplate.exchange(
"http://localhost:8080/XXX/j_spring_security_check",
HttpMethod.POST,
httpEntity,
HttpServletResponse.class);
}
这不起作用我得到:500内部服务器错误原因?
第二个选项 - 我做了以下事情:
public void test2(loginDTO loginDTO, HttpServletRequest request) {
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(
loginDTO.getJ_username(),
loginDTO.getJ_password());
token.setDetails(new WebAuthenticationDetails(request));
Authentication authentication = this.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
this.sessionRegistry.registerNewSession(
request.getSession().getId(),
authentication.getPrincipal());
}
问题是没有调用onAuthenticationSuccess。而且感觉不对,我错过了使用弹簧安全的重点。
正确的原因是什么?
答案 0 :(得分:2)
我通常会执行以下操作:
@Controller
public class AuthenticationController
{
@Autowired
AuthenticationManager authenticationManager;
@Autowired
SecurityContextRepository securityContextRepository;
@RequestMapping(method = Array(RequestMethod.POST), value = Array("/authenticate"))
public String authenticate(@RequestParam String username, @RequestParam String password, HttpServletRequest request, HttpServletResponse response)
{
Authentication result = this.authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
SecurityContextHolder.getContext.setAuthentication(result);
this.securityContextRepository.saveContext(SecurityContextHolder.getContext(), request, response);
return "successView";
}
}
使用这种方法的原因是:
答案 1 :(得分:0)
如果您希望尽可能使用正常的身份验证流程,则可以创建包含登录信息的模拟HttpServletRequest
和HttpServletResponse
(org.springframework.mock.web.MockHttpServletRequest
和org.springframework.mock.web.MockHttpServletResponse
)和密码,然后调用
UsernamePasswordAuthenticationFilter.attemptAuthentication(
HttpServletRequest request,
HttpServletResponse response)`
之后您还需要调用SessionAuthenticationStrategy.onAuthentication(..)
和successfulAuthentication(..)
这有点棘手,因为私有文件,所以这是我的解决方案:
public class ExtendedUsernamePasswordAuthenticationFilter
extends UsernamePasswordAuthenticationFilter {
@Override
public void manualAuthentication(String login,
String password,
HttpServletRequest httpServletRequest)
throws IOException, ServletException {
/** I do not mock the request, I use the existing request and
manipulate them*/
AddableHttpRequest addableHttpRequest =
new AddableHttpRequest(httpServletRequest);
addableHttpRequest.addParameter("j_username", login);
addableHttpRequest.addParameter("j_password", password);
MockHttpServletResponse mockServletResponse =
new MockHttpServletResponse();
Authentication authentication = this.attemptAuthentication(
addableHttpRequest,
mockServletResponse);
this.reflectSessionStrategy().onAuthentication(
authentication,
addableHttpRequest,
mockServletResponse);
this.successfulAuthentication(addableHttpRequest,
mockServletResponse,
authentication);
}
private SessionAuthenticationStrategy reflectSessionStrategy() {
Field sessionStrategyField =
ReflectionUtils.findField(
AbstractAuthenticationProcessingFilter.class,
"sessionStrategy",
SessionAuthenticationStrategy.class);
ReflectionUtils.makeAccessible(sessionStrategyField);
return (SessionAuthenticationStrategy)
ReflectionUtils.getField(sessionStrategyField, this);
}
}
AddableHttpRequest
就像一个基于真实请求的模拟
public class AddableHttpRequest extends HttpServletRequestWrapper {
/** The params. */
private HashMap<String, String> params = new HashMap<String, String>();
public AddableHttpRequest(HttpServletRequest request) {
super(request);
}
@Override
public String getMethod() {
return "POST";
}
@Override
public String getParameter(final String name) {
// if we added one, return that one
if (params.get(name) != null) {
return params.get(name);
}
// otherwise return what's in the original request
return super.getParameter(name);
}
public void addParameter(String name, String value) {
params.put(name, value);
}
}
另一种方法是实现自己的身份验证过滤器。这是一个调用AuthenticationManager.authenticate(Authentication authentication)
的类。但是这个类还负责调用身份验证的所有内容(AbstractAuthenticationProcessingFilter.doFilter
做什么)`
答案 2 :(得分:0)
好的,所以我将@Ralph和@manish的答案结合起来,这就是我所做的:
(twoFactorAuthenticationFilter是UsernamePasswordAuthenticationFilter的扩展名)
public void manualAuthentication(loginDTO loginDTO, HttpServletRequest request, HttpServletResponse response) throws IOException,
ServletException {
AddableHttpRequest addableHttpRequest = new AddableHttpRequest(
request);
addableHttpRequest.addParameter(
"j_username",
loginDTO.getJ_username());
addableHttpRequest.addParameter(
"j_password",
loginDTO.getJ_password());
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) twoFactorAuthenticationFilter.attemptAuthentication(
addableHttpRequest,
response);
if (token.isAuthenticated()) {
twoFactorAuthenticationFilter.successfulAuthentication(
addableHttpRequest,
response,
null,
token);
}
}
工作正常