I've added to my storefront a new extension based on commercewebservices and I've tested several sample services directly through swagger and the ones that doesn't need any kind of authorization works perfect. However, the webservices annotated with #ApiBaseSiteIdAndUserIdParam when I set the userId and siteParam the controller that interecepts this petition doesn't set in session the user I pass, it always returns anonymous user. I've tried creating special OAuth credentials but it doesn't work it always returns anonymous user.
#Secured({ "ROLE_CUSTOMERGROUP", "ROLE_TRUSTED_CLIENT", "ROLE_CUSTOMERMANAGERGROUP" })
#GetMapping(value = "/test")
#ResponseBody
#ApiBaseSiteIdAndUserIdParam
public TestListWsDTO getTest(
#RequestParam(required = false, defaultValue = DEFAULT_FIELD_SET) final String fields) {
final CustomerData customerData = customerFacade.getCurrentCustomer();
if (userFacade.isAnonymousUser()) {
throw new AccessDeniedException("Anonymous user is not allowed");
}
The test#test.com is a registered user.
Why the customer I indicate through swagger is not being captured by customerFacade.getCurrentCustomer() and it always return anonymous?
AS per #Neil it's correct in case of OCC V2 context user is getting determined by the OAuth token.
For OCC there are also configured filters which used to configure or put user users in session if it got found otherwise it will set anonymous.
Please have a look of UserMatchingFilter.
/*
* [y] hybris Platform
*
* Copyright (c) 2017 SAP SE or an SAP affiliate company. All rights reserved.
*
* This software is the confidential and proprietary information of SAP
* ("Confidential Information"). You shall not disclose such Confidential
* Information and shall use it only in accordance with the terms of the
* license agreement you entered into with SAP.
*/
package de.hybris.platform.ycommercewebservices.v2.filter;
import de.hybris.platform.core.model.user.UserModel;
import de.hybris.platform.servicelayer.exceptions.UnknownIdentifierException;
import de.hybris.platform.servicelayer.session.SessionService;
import de.hybris.platform.servicelayer.user.UserService;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
/**
* Filter that puts user from the requested url into the session.
*/
public class UserMatchingFilter extends AbstractUrlMatchingFilter
{
public static final String ROLE_ANONYMOUS = "ROLE_ANONYMOUS";
public static final String ROLE_CUSTOMERGROUP = "ROLE_CUSTOMERGROUP";
public static final String ROLE_CUSTOMERMANAGERGROUP = "ROLE_CUSTOMERMANAGERGROUP";
public static final String ROLE_TRUSTED_CLIENT = "ROLE_TRUSTED_CLIENT";
private static final String CURRENT_USER = "current";
private static final String ANONYMOUS_USER = "anonymous";
private static final String ACTING_USER_UID = "ACTING_USER_UID";
private static final Logger LOG = Logger.getLogger(UserMatchingFilter.class);
private String regexp;
private UserService userService;
private SessionService sessionService;
#Override
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response,
final FilterChain filterChain) throws ServletException, IOException
{
final Authentication auth = getAuth();
if (hasRole(ROLE_CUSTOMERGROUP, auth) || hasRole(ROLE_CUSTOMERMANAGERGROUP, auth))
{
getSessionService().setAttribute(ACTING_USER_UID, auth.getPrincipal());
}
final String userID = getValue(request, regexp);
if (userID == null)
{
if (hasRole(ROLE_CUSTOMERGROUP, auth) || hasRole(ROLE_CUSTOMERMANAGERGROUP, auth))
{
setCurrentUser((String) auth.getPrincipal());
}
else
{
// fallback to anonymous
setCurrentUser(userService.getAnonymousUser());
}
}
else if (userID.equals(ANONYMOUS_USER))
{
setCurrentUser(userService.getAnonymousUser());
}
else if (hasRole(ROLE_TRUSTED_CLIENT, auth) || hasRole(ROLE_CUSTOMERMANAGERGROUP, auth))
{
setCurrentUser(userID);
}
else if (hasRole(ROLE_CUSTOMERGROUP, auth))
{
if (userID.equals(CURRENT_USER) || userID.equals(auth.getPrincipal()))
{
setCurrentUser((String) auth.getPrincipal());
}
else
{
throw new AccessDeniedException("Access is denied");
}
}
else
{
// could not match any authorized role
throw new AccessDeniedException("Access is denied");
}
filterChain.doFilter(request, response);
}
protected Authentication getAuth()
{
return SecurityContextHolder.getContext().getAuthentication();
}
protected String getRegexp()
{
return regexp;
}
#Required
public void setRegexp(final String regexp)
{
this.regexp = regexp;
}
protected UserService getUserService()
{
return userService;
}
#Required
public void setUserService(final UserService userService)
{
this.userService = userService;
}
protected SessionService getSessionService()
{
return sessionService;
}
#Required
public void setSessionService(final SessionService sessionService)
{
this.sessionService = sessionService;
}
protected boolean hasRole(final String role, final Authentication auth)
{
if (auth != null)
{
for (final GrantedAuthority ga : auth.getAuthorities())
{
if (ga.getAuthority().equals(role))
{
return true;
}
}
}
return false;
}
protected void setCurrentUser(final String uid)
{
try
{
final UserModel userModel = userService.getUserForUID(uid);
userService.setCurrentUser(userModel);
}
catch (final UnknownIdentifierException ex)
{
LOG.debug(ex.getMessage());
throw ex;
}
}
protected void setCurrentUser(final UserModel user)
{
userService.setCurrentUser(user);
}
}
In an OCC context, the current user is determined by the OAuth token. If you just have the client credentials then you are Anonymous. However after a user-specific login you have a different token that correlates to the user that authenticated with OAuth. There should be a filter in the commercewebservices stack that checks the token & maps to a current user in the temporary session. As far as I'm aware only that user will appear as the current customer, not the one passed in the URL ... you probably want to check that the current customer is the same user (or should have permission to see details about that user)
If you are using OCC web services, these services are stateless. So you cannot get any values from session variables. Generally facades using by storefront and storefronts are using sessions.
Related
I am trying to extend keycloak by creating a new endpoint to authenticate users.
The point is, user is not stored in keycloak, the user is stored in an external system.
The external system will call the new endpoint and provide token (will contains user info), clientId, and clientSecret. and (somehow) we will verify the existence of the user.
The challenge I am facing right now is I cannot create a session for the user. (seems the session requires existed user in keycloak, I am using InMemoryUser)
package com.mhewedy.auth;
import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.Config;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.*;
import org.keycloak.protocol.AuthorizationEndpointBase;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.services.resource.RealmResourceProvider;
import org.keycloak.services.resource.RealmResourceProviderFactory;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.util.DefaultClientSessionContext;
import org.keycloak.storage.adapter.InMemoryUserAdapter;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.UUID;
import static org.keycloak.services.resources.Cors.ACCESS_CONTROL_ALLOW_ORIGIN;
import static org.keycloak.utils.MediaType.APPLICATION_JSON_TYPE;
public class MyEndpoint extends AuthorizationEndpointBase implements RealmResourceProvider {
private final Logger logger = Logger.getLogger(MyEndpoint.class);
private final TokenManager tokenManager = new TokenManager();
public MyEndpoint(RealmModel realm, EventBuilder event) {
super(realm, event);
}
#Override
public Object getResource() {
return this;
}
#Override
public void close() {
}
#GET
#Path("authenticate")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response authenticate(#FormParam("token") String token,
#FormParam("client_id") String clientId,
#FormParam("client_secret") String clientSecret,
#Context HttpRequest request) {
// validate client_id & client_secret
// validate token
logger.info("generating access token...");
String userId = UUID.randomUUID().toString();
UserModel userModel =
new InMemoryUserAdapter(session, session.getContext().getRealm(), userId);
userModel.setUsername(token);
userModel.setEnabled(true);
// this session object doesn't contain the userModel, cause it seems it lookups the user by id and doesn't find it
UserSessionModel userSession = session.sessions().createOfflineUserSession(session.sessions().createUserSession(
session.getContext().getRealm(),
userModel,
token,
"192.168.1.1",
"My",
false,
null,
null
));
ClientModel clientModel = realm.getClientByClientId(clientId);
logger.infof("Configurable token requested for username=%s and client=%s on realm=%s",
userModel.getUsername(), clientModel.getClientId(), realm.getName());
AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, clientModel, userSession);
ClientSessionContext clientSessionContext =
DefaultClientSessionContext.fromClientSessionScopeParameter(clientSession, session);
AccessToken newToken = tokenManager
.createClientAccessToken(session, realm, clientModel, userModel, userSession, clientSessionContext);
newToken.expiration(10 * 10 * 10 * 10);
EventBuilder eventBuilder = new EventBuilder(realm, session, session.getContext().getConnection());
AccessTokenResponse response = tokenManager
.responseBuilder(realm, clientModel, eventBuilder, session, userSession, clientSessionContext)
.accessToken(newToken)
.build();
return buildCorsResponse(request, response);
}
private Response buildCorsResponse(#Context HttpRequest request, AccessTokenResponse response) {
Cors cors = Cors.add(request)
.auth()
.allowedMethods("POST")
.auth()
.exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_ORIGIN)
.allowAllOrigins();
return cors.builder(Response.ok(response).type(APPLICATION_JSON_TYPE)).build();
}
// ----------------------------------------------------------------------------------------------------------------
public static class MyEndpointFactory implements RealmResourceProviderFactory {
#Override
public RealmResourceProvider create(KeycloakSession session) {
KeycloakContext context = session.getContext();
RealmModel realm = context.getRealm();
EventBuilder event = new EventBuilder(realm, session, context.getConnection());
MyEndpoint provider = new MyEndpoint(realm, event);
ResteasyProviderFactory.getInstance().injectProperties(provider);
return provider;
}
#Override
public void init(Config.Scope config) {
}
#Override
public void postInit(KeycloakSessionFactory factory) {
}
#Override
public void close() {
}
#Override
public String getId() {
return "MyEndpoint";
}
}
}
I am using code from here but the use case is differnt.
I solved by saving the user in the cache (db) if not exist:
String username = getUsernameFromToken(token);
String userId = "my-" + username;
UserModel userModel = new InMemoryUserAdapter(session, session.getContext().getRealm(), username);
userModel.setUsername(username);
userModel.setEnabled(true);
if (session.users().getUserById(realm, userId) == null) {
session.userCache().addUser(realm, userId, username, false, false);
}
I have adapted the code from here to call a MitreID OIDC server.
My controller:
public final String home(Principal p) {
final String username = SecurityContextHolder.getContext().getAuthentication().getName();
...
returns null and is null for all userdetails.
I have also tried:
public final String home(#AuthenticationPrincipal OpenIdConnectUserDetails user) {
final String username = user.getUsername();
and
#RequestMapping(value = "/username", method = RequestMethod.GET)
#ResponseBody
public String currentUserNameSimple(HttpServletRequest request) {
Principal principal = request.getUserPrincipal();
return "username: " + principal.getName();
}
Everything is null but the authentication is returning an access and user token.
My security config is:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private OAuth2RestTemplate restTemplate;
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
#Bean
public OpenIdConnectFilter myFilter() {
final OpenIdConnectFilter filter = new OpenIdConnectFilter("/openid_connect_login");
filter.setRestTemplate(restTemplate);
return filter;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.addFilterAfter(new OAuth2ClientContextFilter(), AbstractPreAuthenticatedProcessingFilter.class)
.addFilterAfter(myFilter(), OAuth2ClientContextFilter.class)
.httpBasic().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/openid_connect_login"))
.and()
.authorizeRequests()
.antMatchers("/","/index*").permitAll()
.anyRequest().authenticated()
;
// #formatter:on
}
}
So why can my controller not access the userdetails?
EDIT: as requested, OpenIdConnectFilter:
package org.baeldung.security;
import java.io.IOException;
import java.net.URL;
import java.security.interfaces.RSAPublicKey;
import java.util.Date;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.security.oauth2.client.OAuth2RestOperations;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import com.auth0.jwk.Jwk;
import com.auth0.jwk.JwkProvider;
import com.auth0.jwk.UrlJwkProvider;
import com.fasterxml.jackson.databind.ObjectMapper;
public class OpenIdConnectFilter extends AbstractAuthenticationProcessingFilter {
#Value("${oidc.clientId}")
private String clientId;
#Value("${oidc.issuer}")
private String issuer;
#Value("${oidc.jwkUrl}")
private String jwkUrl;
public OAuth2RestOperations restTemplate;
public OpenIdConnectFilter(String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
setAuthenticationManager(new NoopAuthenticationManager());
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
OAuth2AccessToken accessToken;
logger.info("ewd here: b " );
try {
accessToken = restTemplate.getAccessToken();
} catch (final OAuth2Exception e) {
throw new BadCredentialsException("Could not obtain access token", e);
}
try {
logger.info("ewd access token: " + accessToken);
final String idToken = accessToken.getAdditionalInformation().get("id_token").toString();
String kid = JwtHelper.headers(idToken)
.get("kid");
final Jwt tokenDecoded = JwtHelper.decodeAndVerify(idToken, verifier(kid));
final Map<String, String> authInfo = new ObjectMapper().readValue(tokenDecoded.getClaims(), Map.class);
verifyClaims(authInfo);
final OpenIdConnectUserDetails user = new OpenIdConnectUserDetails(authInfo, accessToken);
logger.info("ewd user token: " + tokenDecoded);
return new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
} catch (final Exception e) {
throw new BadCredentialsException("Could not obtain user details from token", e);
}
}
public void verifyClaims(Map claims) {
int exp = (int) claims.get("exp");
Date expireDate = new Date(exp * 1000L);
Date now = new Date();
if (expireDate.before(now) || !claims.get("iss").equals(issuer) || !claims.get("aud").equals(clientId)) {
throw new RuntimeException("Invalid claims");
}
}
private RsaVerifier verifier(String kid) throws Exception {
JwkProvider provider = new UrlJwkProvider(new URL(jwkUrl));
Jwk jwk = provider.get(kid);
return new RsaVerifier((RSAPublicKey) jwk.getPublicKey());
}
public void setRestTemplate(OAuth2RestTemplate restTemplate2) {
restTemplate = restTemplate2;
}
private static class NoopAuthenticationManager implements AuthenticationManager {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
throw new UnsupportedOperationException("No authentication should be done with this AuthenticationManager");
}
}
}
In the tutorial you refer to, Google OpenId Connect is used. This service also returns the extra scope email variable that is read in:
OpenIdConnectUserDetails.class
public OpenIdConnectUserDetails(Map<String, String> userInfo, OAuth2AccessToken token) {
this.userId = userInfo.get("sub");
this.username = userInfo.get("email");
this.token = token;
}
Without knowing the specific configuration of your MitreID OIDC server maybe the openId server is not returning the email variable
I am a new auth0 user and had the same issue, trying to get some User information in the Spring Controller. I am able to access the Claims from the token using this code.
#GetMapping
public List<Task> getTasks(AuthenticationJsonWebToken authentication) {
logger.debug("getTasks called.");
DecodedJWT jwt = JWT.decode(authentication.getToken());
Map<String, Claim> claims = jwt.getClaims();
for (Object key: claims.keySet()) {
logger.debug("key: {}, value: {}", key.toString(), claims.get(key).asString());
}
return taskRepository.findAll();
}
Hope this helps.
If you need username, you can get it from JwtAuthenticationToken object as below:
#GetMapping("/home")
public String home(JwtAuthenticationToken user) {
String name = user.getName();
If you need some other information from user's profile, you can call your auth server's /userinfo endpoint with the access token as below:
This will fetch info only if you had included profile scope in your authorize call.
#GetMapping("/home")
public String home(JwtAuthenticationToken user) {
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.AUTHORIZATION, "Bearer "+user.getToken().getTokenValue());
HttpEntity entity = new HttpEntity(headers);
ResponseEntity<Map> userinfo = template.exchange("https://your-auth-server/default/v1/userinfo", HttpMethod.GET, entity, Map.class);
String name = (String) userinfo.getBody().get("given_name");
You can retrieve all profile attributes from this response.
For Auth0, I was able to get user information in two ways:
First one is using JwtAuthenticationToken directly on the controller as shown below.
#GetMapping("/info")
public void users(JwtAuthenticationToken token) {
System.out.println(token.getName());
System.out.println(token.getTokenAttributes().get("name"));
}
Here, token.getName() prints the user id from auth0 and token.getTokenAttributes() returns a map from which we can retrieve any information that we need. For example to print user name, we can use token.getTokenAttributes().get("name").
Casting Authentication object to JwtAuthenticationToken:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
JwtAuthenticationToken token = (JwtAuthenticationToken) authentication;
String username = token.getTokenAttributes().get("name");
you can retrieve the user or profile related properties that were defined when creating the okta oidc application through the OidcUser class, which can be used with the AuthenticationPrincipal annotation.Follow below steps
**My Controller:**
#GetMapping("/user")
public User user(#AuthenticationPrincipal OidcUser oidcUser) {
System.out.println("oidcUser :: " + oidcUser + "\n\n");
User user = new User();
System.out.println("Attributes :: " + oidcUser.getAttributes() + "\n\n");
user.setFirstName(oidcUser.getAttribute("given_name"));
user.setLastName(oidcUser.getAttribute("family_name"));
user.setName(oidcUser.getAttribute("name"));
user.setPreferred_username(oidcUser.getAttribute("preferred_username"));
user.setEmail(oidcUser.getAttribute("email"));
user.setGroups(getGroupsFromCurrentUser());
System.out.println(user.toString() + "\n\n");
return user;
}
private List<String> getGroupsFromCurrentUser() {
List<String> groups = new ArrayList<>();
Collection<? extends GrantedAuthority> authorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities();
System.out.println("\n\n"+authorities+"\n\n");
for (GrantedAuthority auth : authorities) {
groups.add(auth.getAuthority());
}
System.out.println("\n\n"+"groups :: "+groups+"\n\n");
return groups;
}
I am trying to integrate Google sign-in into an existing Spring security application. The goal is to have a Google sign-in button that will allow a user to log in along with the standard login using the username/password combination.
Based on the guide that Google provides (https://developers.google.com/identity/sign-in/web/backend-auth) it looks like all I need to do is extend the login form (that currently only has the login and the password input fields) with an extra field "id_token" and submit it to the server.
Would it be a good security practice? I searched the web and I am surprised I cannot find any similar implementations on the web.
Here is my take on the required spring-security components:
filter:
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.util.Assert;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class GoogleIdAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private static final long serialVersionUID = 1L;
private String tokenParamName = "googleIdToken";
/**
* Creates an instance which will authenticate against the supplied
* {#code AuthenticationManager} and which will ignore failed authentication attempts,
* allowing the request to proceed down the filter chain.
*
* #param authenticationManager the bean to submit authentication requests to
* #param defaultFilterProcessesUrl the url to check for auth requests on (e.g. /login/google)
*/
public GoogleIdAuthenticationFilter(AuthenticationManager authenticationManager, String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
Assert.notNull(authenticationManager, "authenticationManager cannot be null");
setAuthenticationManager(authenticationManager);
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
String token = request.getParameter(tokenParamName);
if (token == null) {
return null;
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("Google ID Token Authorization parameter found with value '" + token + "'");
}
Object details = this.authenticationDetailsSource.buildDetails(request);
GoogleIdAuthenticationToken authRequest = new GoogleIdAuthenticationToken(token, details);
Authentication authResult = getAuthenticationManager().authenticate(authRequest);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Authentication success: " + authResult);
}
return authResult;
}
public String getTokenParamName() {
return tokenParamName;
}
public void setTokenParamName(String tokenParamName) {
this.tokenParamName = tokenParamName;
}
}
authentication provider:
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.apache.ApacheHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import javax.annotation.Resource;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Collections;
public class GoogleIdAuthenticationProvider implements AuthenticationProvider {
private static final Logger logger = LoggerFactory.getLogger(GoogleIdAuthenticationProvider.class);
private String clientId;
#Resource
private UserDetailsService userDetailsService;
private HttpTransport httpTransport = new ApacheHttpTransport();
private JsonFactory jsonFactory = new JacksonFactory();
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (!supports(authentication.getClass())) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("This authentication provider does not support instances of type %s", authentication.getClass().getName()));
}
return null;
}
GoogleIdAuthenticationToken googleIdAuthenticationToken = (GoogleIdAuthenticationToken) authentication;
if (logger.isDebugEnabled())
logger.debug(String.format("Validating google login with token '%s'", googleIdAuthenticationToken.getCredentials()));
GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(httpTransport, jsonFactory)
.setAudience(Collections.singletonList(getClientId()))
.build();
GoogleIdToken googleIdToken = null;
try {
googleIdToken = verifier.verify((String) googleIdAuthenticationToken.getCredentials());
if (googleIdToken == null) {
throw new BadCredentialsException("Unable to verify token");
}
} catch (IOException|GeneralSecurityException e) {
throw new BadCredentialsException("Unable to verify token", e);
}
Payload payload = googleIdToken.getPayload();
// Get profile information from payload
String email = payload.getEmail();
if (logger.isDebugEnabled()) {
logger.debug(String.format("Loading user details for email '%s'", email));
}
UserDetails userDetails = null;
try {
userDetails = userDetailsService.loadUserByUsername(email);
if (!userDetails.isAccountNonLocked()) {
throw new LockedException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.locked", "User account is locked"));
}
if (!userDetails.isEnabled()) {
throw new DisabledException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.disabled", "User is disabled"));
}
if (!userDetails.isAccountNonExpired()) {
throw new AccountExpiredException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.expired", "User account has expired"));
}
} catch (UsernameNotFoundException e) {
// provision a new user?
throw e;
}
return new GoogleIdAuthenticationToken((String) googleIdAuthenticationToken.getCredentials(), userDetails.getUsername(), userDetails.getAuthorities(), authentication.getDetails());
}
#Override
public boolean supports(Class<? extends Object> authentication) {
return (GoogleIdAuthenticationToken.class.isAssignableFrom(authentication));
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
}
token:
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import java.util.ArrayList;
import java.util.Collection;
public class GoogleIdAuthenticationToken extends AbstractAuthenticationToken {
private String credentials;
private Object principal;
public GoogleIdAuthenticationToken(String token, Object details) {
super(new ArrayList<>());
this.credentials = token;
setDetails(details);
setAuthenticated(false);
}
GoogleIdAuthenticationToken(String token, String principal, Collection<? extends GrantedAuthority> authorities, Object details) {
super(authorities);
this.credentials = token;
this.principal = principal;
setDetails(details);
setAuthenticated(true);
}
#Override
public Object getCredentials() {
return credentials;
}
#Override
public Object getPrincipal() {
return principal;
}
}
After plugging in the above you'll just need to POST to "/login/google" (or whatever you've configured) with the token returned by Google in the 'googleIdToken' (or whatever you've configured).
Here I am sharing some code which I have used for server-side Google sign token verification using spring. It is a working example :
#Autowired
HttpTransport transport;
private static final JsonFactory jsonFactory = new JacksonFactory();
public void verify(String idTokenString)
{
GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory).setAudience(Collections.singletonList(GOOGLE_CLIENT_ID))
.build();
GoogleIdToken idToken = verifier.verify(idTokenString);
IdToken.Payload payload = idToken.getPayload();
Boolean emailVerified = (Boolean) payload.get("email_verified");
if (if (idToken != null) {
String email = (String) payload.get("email");
String fname = (String) payload.get("given_name");
String pictureUrl = (String) payload.get("picture");
String lname = (String) payload.get("family_name");
}
Dependencies:
<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client</artifactId>
<version>1.31.1</version>
</dependency>
<dependency>
<groupId>com.google.http-client</groupId>
<artifactId>google-http-client</artifactId>
<version>1.40.0</version>
</dependency>
So, the right answer turned out to be not extending the existing auth filter/provider but defining/adding another {Token Authentication class + token auth filter + token auth provider (provider is kind of optional)}
Common question
Is is possible to send get reguest from Java servlet's doGet method ? I need to check some "ticket" against my Web API .NET service, so can I call to this service from my custom servlet in the doGet method ?
public class IdentityProviderServlet extends HttpServlet {
...
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
...
// make request to Web API and save received user name to response
...
}
Details
We have web-app (.NET, MVC5) that is used TIBCO Spotfire 7.0 as engine of analysis reports.
To allow our users to see reports inside web-app we use Spotfire WebPlayer (IIS web app) and JavaScript API. We authenticate our users in web app and then they are allowed to get request to WebPlayer leveraging JS API. In order to use already authenticated users we implemented custom authentication at WebPlayer based on keys-tickets as described here. Thus we created .NET assembly that is loaded by Spotfire WebPlayer and calls overridden function. In this function we call Web API service to validate user and get valid spotfire username, then I create IIdentity with received username.
When new version of TIBCO Spotfire 7.5 released we discovered that they removed support of custom authentication, because they changed architecture and now they support "external authentication". This approach could be implemented as as Java servlet that is used to authenticate user and then spotfire:
Retrieves the user name from the getUserPrincipal() method of
javax.servlet.http.HttpServletRequest
All this forced us to re-write our logic in Java. However we don't want to change overall workflow of our authentication and we want to stick to already working ticketing schema. I'm new to Java servlets, so my goal to implement the same authentication based on servlet. They have example where servlet class has methods doGet and doPost (link to zip with example). My assumption here that I can implement my own doGet and send request to Web API to validate ticket and get back username.
Does it make sense ?
Finally I end up with this code. I implemented simple filter instead of servlet.
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import java.io.*;
import org.json.*;
public class Authenticator implements IAuthenticator {
#Override
public IIdentity doAuthentication(String pathToAuthIdentity) throws IOException {
try {
// Create an instance of HttpClient.
HttpClient httpClient = HttpClients.createDefault();
// Create a method instance.
HttpGet get = new HttpGet(pathToAuthIdentity);
HttpResponse response = httpClient.execute(get);
int internResponseStatus = response.getStatusLine().getStatusCode();
if(200 == internResponseStatus)
{
BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuffer result = new StringBuffer();
String line = "";
while ((line = rd.readLine()) != null) {
result.append(line);
}
String userName = null;
try {
JSONObject obj = new JSONObject(result.toString());
userName = obj.getString("SpotfireUser");
} catch (JSONException ex) {
}
return new Identity(userName);
}else
{
return new AIdentity(null);
}
} catch (IOException ex) {
throw ex;
}
}
public class AIdentity implements IIdentity
{
private final String UserName;
public AIdentity(String userName)
{
this.UserName = userName;
}
#Override
public String getName() {
return UserName;
}
}
}
And that's how I use this class
import java.io.IOException;
import java.security.Principal;
import javax.servlet.http.*;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class SpotfireAuthFilter implements Filter {
private static final String AUTHENTICATION_SERVICE_URL_PARAM = "AUTHENTICATION_SERVICE_URL";
private static final String COOKIE_NAME_PARAM = "COOKIE_NAME";
private ServletContext context;
private String[] SpotfireTicketNames = null;
private String[] AuthServiceBaseURLs = null;
private IAuthenticator AuthService;
#Override
public void init(FilterConfig fc) throws ServletException {
context = fc.getServletContext();
if(null == fc.getInitParameter(AUTHENTICATION_SERVICE_URL_PARAM)
|| null == fc.getInitParameter(COOKIE_NAME_PARAM) )
{
throw new ServletException("Can't read filter initial parameters");
}
AuthServiceBaseURLs = fc.getInitParameter(AUTHENTICATION_SERVICE_URL_PARAM).split(",");
SpotfireTicketNames = fc.getInitParameter(COOKIE_NAME_PARAM).split(",");
AuthService = new Authenticator();
if(SpotfireTicketNames.length != AuthServiceBaseURLs.length)
{
throw new ServletException(
String.format("Count of '%s' parameter don't equal '%s' parameter",
COOKIE_NAME_PARAM,
AUTHENTICATION_SERVICE_URL_PARAM));
}
}
#Override
public final void doFilter(
ServletRequest servletRequest,
ServletResponse servletResponse,
FilterChain chain) throws ServletException
{
final HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpServletResponse response = (HttpServletResponse) servletResponse;
try
{
doFilter(request, response, chain);
}
catch (IOException | RuntimeException e)
{
// Not possible to authenticate, return a 401 Unauthorized status code without any WWW-Authenticate header
sendError(response, 401, "Unauthorized");
}
}
#Override
public void destroy() {
// do nothing
}
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException
{
String url = getAuthServiceURL(request);
if(null != url)
{
IIdentity identity = AuthService.doAuthentication(url);
if(null != identity)
{
String userName = identity.getName();
if(null != userName && !userName.equalsIgnoreCase(""))
{
Principal principal = createPrincipal(userName);
// Pass on the request to the filter chain and the authentication framework
// should pick up this priincipal and authenticate user
chain.doFilter(new WrappedHttpServletRequest(request, principal), response);
}
else
{
throw new IOException("Authentication failed");
}
}else
{
throw new IOException("Can't authenticate user by url " + url);
}
}
else
{
throw new IOException("Can't find ticket to authenticate user.");
}
// Done!
return;
}
private void sendError(HttpServletResponse response, int statusCode, String message) {
try {
response.sendError(statusCode, message);
} catch (IOException e) {
}
}
private String getAuthServiceURL(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
for(int i =0; i< cookies.length; ++i)
{
for(int j =0; j< SpotfireTicketNames.length; ++j)
{
if(cookies[i].getName().equalsIgnoreCase(SpotfireTicketNames[j]))
{
return String.format(AuthServiceBaseURLs[j], cookies[i].getValue());
}
}
}
return null;
}
private Principal createPrincipal(String username)
{
// check does username contain domain/email/display name
return new APrincipal(username);
}
/**
* A wrapper for {#link HttpServletRequest} objects.
*/
private static class WrappedHttpServletRequest extends HttpServletRequestWrapper {
private final Principal principal;
public WrappedHttpServletRequest(HttpServletRequest request, Principal principal) {
super(request);
this.principal = principal;
}
#Override
public Principal getUserPrincipal() {
return this.principal;
}
} // WrappedHttpServletRequest
}
public class APrincipal implements Principal {
private final String _username;
public APrincipal(String username) {
_username = username;
}
#Override
public String getName() {
return _username;
}
}
And these initial parameters
You can use the library Apache HTTP Components
Short example for doGet() (I didn't compile it):
import org.apache.http.*;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
{
String url="http://your.server.com/path/to/app?par1=yxc&par2=abc");
HttpGet get=new HttpGet(url);
httpClient = HttpClients.createDefault();
// optional configuration
RequestConfig config=RequestConfig.custom().setSocketTimeout(socketTimeoutSec * 1000).build();
// more configuration
get.setConfig(config);
CloseableHttpResponse internResponse = httpClient.execute(get);
int internResponseStatus = internResponse.getStatusLine().getStatusCode();
InputStream respIn = internResponseEntity.getContent();
String contentType = internResponseEntity.getContentType().getValue();
// consume the response
}
This works fine until I have to test a service that needs a logged in user, how do I add user to context :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:applicationContext-test.xml")
#WebAppConfiguration
public class FooTest {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#Resource(name = "aService")
private AService aService; //uses logged in user
#Before
public void setup() {
this.mockMvc = webAppContextSetup(this.webApplicationContext).build();
}
If you want to use MockMVC with the latest spring security test package, try this code:
Principal principal = new Principal() {
#Override
public String getName() {
return "TEST_PRINCIPAL";
}
};
getMockMvc().perform(get("http://your-url.com").principal(principal))
.andExpect(status().isOk()));
Keep in mind that you have to be using Principal based authentication for this to work.
If successful authentication yields some cookie, then you can capture that (or just all cookies), and pass it along in the next tests:
#Autowired
private WebApplicationContext wac;
#Autowired
private FilterChainProxy filterChain;
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
.addFilter(filterChain).build();
}
#Test
public void testSession() throws Exception {
// Login and save the cookie
MvcResult result = mockMvc.perform(post("/session")
.param("username", "john").param("password", "s3cr3t")).andReturn();
Cookie c = result.getResponse().getCookie("my-cookie");
assertThat(c.getValue().length(), greaterThan(10));
// No cookie; 401 Unauthorized
mockMvc.perform(get("/personal").andExpect(status().isUnauthorized());
// With cookie; 200 OK
mockMvc.perform(get("/personal").cookie(c)).andExpect(status().isOk());
// Logout, and ensure we're told to wipe the cookie
result = mockMvc.perform(delete("/session").andReturn();
c = result.getResponse().getCookie("my-cookie");
assertThat(c.getValue().length(), is(0));
}
Though I know I'm not making any HTTP requests here, I kind of like the stricter separation of the above integration test and my controllers and Spring Security implementation.
To make the code a bit less verbose, I use the following to merge the cookies after making each request, and then pass those cookies along in each subsequent request:
/**
* Merges the (optional) existing array of Cookies with the response in the
* given MockMvc ResultActions.
* <p>
* This only adds or deletes cookies. Officially, we should expire old
* cookies. But we don't keep track of when they were created, and this is
* not currently required in our tests.
*/
protected static Cookie[] updateCookies(final Cookie[] current,
final ResultActions result) {
final Map<String, Cookie> currentCookies = new HashMap<>();
if (current != null) {
for (Cookie c : current) {
currentCookies.put(c.getName(), c);
}
}
final Cookie[] newCookies = result.andReturn().getResponse().getCookies();
for (Cookie newCookie : newCookies) {
if (StringUtils.isBlank(newCookie.getValue())) {
// An empty value implies we're told to delete the cookie
currentCookies.remove(newCookie.getName());
} else {
// Add, or replace:
currentCookies.put(newCookie.getName(), newCookie);
}
}
return currentCookies.values().toArray(new Cookie[currentCookies.size()]);
}
...and a small helper as cookie(...) needs at least one cookie:
/**
* Creates an array with a dummy cookie, useful as Spring MockMvc
* {#code cookie(...)} does not like {#code null} values or empty arrays.
*/
protected static Cookie[] initCookies() {
return new Cookie[] { new Cookie("unittest-dummy", "dummy") };
}
...to end up with:
Cookie[] cookies = initCookies();
ResultActions actions = mockMvc.perform(get("/personal").cookie(cookies)
.andExpect(status().isUnauthorized());
cookies = updateCookies(cookies, actions);
actions = mockMvc.perform(post("/session").cookie(cookies)
.param("username", "john").param("password", "s3cr3t"));
cookies = updateCookies(cookies, actions);
actions = mockMvc.perform(get("/personal").cookie(cookies))
.andExpect(status().isOk());
cookies = updateCookies(cookies, actions);
You should be able to just add the user to the security context:
List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();
list.add(new GrantedAuthorityImpl("ROLE_USER"));
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(user, password,list);
SecurityContextHolder.getContext().setAuthentication(auth);
Somewhy solution with principal didn't worked for me, thus, I'd like to mention another way out:
mockMvc.perform(get("your/url/{id}", 5).with(user("anyUserName")))
With spring 4 this solution mock the formLogin and logout using sessions and not cookies because spring security test not returning cookies.
Because it's not a best practice to inherit tests you can #Autowire this component in your tests and call it's methods.
With this solution each perform operation on the mockMvc will be called as authenticated if you called the performLogin on the end of the test you can call performLogout.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.stereotype.Component;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import javax.servlet.Filter;
import static com.condix.SessionLogoutRequestBuilder.sessionLogout;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
#Component
public class SessionBasedMockMvc {
private static final String HOME_PATH = "/";
private static final String LOGOUT_PATH = "/login?logout";
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
private Filter springSecurityFilterChain;
private MockMvc mockMvc;
public MockMvc createSessionBasedMockMvc() {
final MockHttpServletRequestBuilder defaultRequestBuilder = get("/dummy-path");
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext)
.defaultRequest(defaultRequestBuilder)
.alwaysDo(result -> setSessionBackOnRequestBuilder(defaultRequestBuilder, result.getRequest()))
.apply(springSecurity(springSecurityFilterChain))
.build();
return this.mockMvc;
}
public void performLogin(final String username, final String password) throws Exception {
final ResultActions resultActions = this.mockMvc.perform(formLogin().user(username).password(password));
this.assertSuccessLogin(resultActions);
}
public void performLogout() throws Exception {
final ResultActions resultActions = this.mockMvc.perform(sessionLogout());
this.assertSuccessLogout(resultActions);
}
private MockHttpServletRequest setSessionBackOnRequestBuilder(final MockHttpServletRequestBuilder requestBuilder,
final MockHttpServletRequest request) {
requestBuilder.session((MockHttpSession) request.getSession());
return request;
}
private void assertSuccessLogin(final ResultActions resultActions) throws Exception {
resultActions.andExpect(status().isFound())
.andExpect(authenticated())
.andExpect(redirectedUrl(HOME_PATH));
}
private void assertSuccessLogout(final ResultActions resultActions) throws Exception {
resultActions.andExpect(status().isFound())
.andExpect(unauthenticated())
.andExpect(redirectedUrl(LOGOUT_PATH));
}
}
Because default LogoutRequestBuilder doesn't support session we need to create another logout request builder.
import org.springframework.beans.Mergeable;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.request.ConfigurableSmartRequestBuilder;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import javax.servlet.ServletContext;
import java.util.ArrayList;
import java.util.List;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
/**
* This is a logout request builder which allows to send the session on the request.<br/>
* It also has more than one post processors.<br/>
* <br/>
* Unfortunately it won't trigger {#link org.springframework.security.core.session.SessionDestroyedEvent} because
* that is triggered by {#link org.apache.catalina.session.StandardSessionFacade#invalidate()} in Tomcat and
* for mocks it's handled by #{{#link MockHttpSession#invalidate()}} so the log out message won't be visible for tests.
*/
public final class SessionLogoutRequestBuilder implements
ConfigurableSmartRequestBuilder<SessionLogoutRequestBuilder>, Mergeable {
private final List<RequestPostProcessor> postProcessors = new ArrayList<>();
private String logoutUrl = "/logout";
private MockHttpSession session;
private SessionLogoutRequestBuilder() {
this.postProcessors.add(csrf());
}
static SessionLogoutRequestBuilder sessionLogout() {
return new SessionLogoutRequestBuilder();
}
#Override
public MockHttpServletRequest buildRequest(final ServletContext servletContext) {
return post(this.logoutUrl).session(session).buildRequest(servletContext);
}
public SessionLogoutRequestBuilder logoutUrl(final String logoutUrl) {
this.logoutUrl = logoutUrl;
return this;
}
public SessionLogoutRequestBuilder session(final MockHttpSession session) {
Assert.notNull(session, "'session' must not be null");
this.session = session;
return this;
}
#Override
public boolean isMergeEnabled() {
return true;
}
#SuppressWarnings("unchecked")
#Override
public Object merge(final Object parent) {
if (parent == null) {
return this;
}
if (parent instanceof MockHttpServletRequestBuilder) {
final MockHttpServletRequestBuilder parentBuilder = (MockHttpServletRequestBuilder) parent;
if (this.session == null) {
this.session = (MockHttpSession) ReflectionTestUtils.getField(parentBuilder, "session");
}
final List postProcessors = (List) ReflectionTestUtils.getField(parentBuilder, "postProcessors");
this.postProcessors.addAll(0, (List<RequestPostProcessor>) postProcessors);
} else if (parent instanceof SessionLogoutRequestBuilder) {
final SessionLogoutRequestBuilder parentBuilder = (SessionLogoutRequestBuilder) parent;
if (!StringUtils.hasText(this.logoutUrl)) {
this.logoutUrl = parentBuilder.logoutUrl;
}
if (this.session == null) {
this.session = parentBuilder.session;
}
this.postProcessors.addAll(0, parentBuilder.postProcessors);
} else {
throw new IllegalArgumentException("Cannot merge with [" + parent.getClass().getName() + "]");
}
return this;
}
#Override
public SessionLogoutRequestBuilder with(final RequestPostProcessor postProcessor) {
Assert.notNull(postProcessor, "postProcessor is required");
this.postProcessors.add(postProcessor);
return this;
}
#Override
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
for (final RequestPostProcessor postProcessor : this.postProcessors) {
request = postProcessor.postProcessRequest(request);
if (request == null) {
throw new IllegalStateException(
"Post-processor [" + postProcessor.getClass().getName() + "] returned null");
}
}
return request;
}
}
After calling the performLogin operation all your request in the test will be automatically performed as logged in user.
Yet another way... I use the following annotations:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#TestExecutionListeners(listeners={ServletTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
WithSecurityContextTestExcecutionListener.class})
#WithMockUser
public class WithMockUserTests {
...
}
(source)