Reputation: 1187
I am using spring security and it works fine, but now I want to start the security process manually, do to client changes I need to get in my controller the user name and password (the form wont call "j_spring_security_check" directly)
I thought of 2 options with both I have some problems:
After I get the parameters and do something I will send a post request to j_spring_security_check url. My code:
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);
}
This doesn't work and I get :500 internal server error why?
second option- I did the following:
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());
}
The problem is that onAuthenticationSuccess is not called. and it feels wrong, that I'm missing the point of using spring security.
What is the correct why?
Upvotes: 2
Views: 3035
Reputation: 1187
OK so I combined @Ralph and @manish answers and this is what I did:
(twoFactorAuthenticationFilter is an extension of 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);
}
}
It works fine
Upvotes: 0
Reputation: 20135
I typically do the following:
@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";
}
}
The reasons for using this approach is:
Upvotes: 3
Reputation: 120781
When you want to use as most as possible from the normal Authentication Process, then you could create a mocked HttpServletRequest
and HttpServletResponse
(org.springframework.mock.web.MockHttpServletRequest
and org.springframework.mock.web.MockHttpServletResponse
) containing login and password, and then invoke
UsernamePasswordAuthenticationFilter.attemptAuthentication(
HttpServletRequest request,
HttpServletResponse response)`
afterwards you will also need to invoke SessionAuthenticationStrategy.onAuthentication(..)
and successfulAuthentication(..)
This is all a bit tricky, because of private fileds, so this is my solution:
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
is like a mock that is based on an real request
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);
}
}
An other way, would be implementing your own, authentication filter. Thats a class that invoke the AuthenticationManager.authenticate(Authentication authentication)
. But this class is also responsible for invoking all the stuff around authentication (what AbstractAuthenticationProcessingFilter.doFilter
does)`
Upvotes: 0