I am working on a Java application which connects to a Spring-MVC server, using Spring-Security for authentication/authorization. The login part works, and I get a JSESSIONID back in the Java application, but when I make request to secured resource, it fails, Spring-Security is unable to find any logged in user. What am I doing wrong here?
security-applicationContext.xml :
<security:http pattern="/resources/**" security="none"/>
<security:http create-session="ifRequired" use-expressions="true" auto-config="false" disable-url-rewriting="true">
<security:form-login login-page="/login" login-processing-url="/j_spring_security_check"
default-target-url="/dashboard" always-use-default-target="false"
authentication-failure-url="/denied"/>
<security:remember-me key="_spring_security_remember_me" user-service-ref="userDetailsService"
token-validity-seconds="1209600" data-source-ref="dataSource"/>
<security:logout delete-cookies="JSESSIONID" invalidate-session="true" logout-url="/j_spring_security_logout"/>
<!--<security:intercept-url pattern="/**" requires-channel="https"/>-->
<security:port-mappings>
<security:port-mapping http="8080" https="8443"/>
</security:port-mappings>
<security:logout logout-url="/logout" logout-success-url="/" success-handler-ref="myLogoutHandler"/>
<security:session-management session-fixation-protection="migrateSession">
<security:concurrency-control session-registry-ref="sessionRegistry" max-sessions="5" expired-url="/login"/>
</security:session-management>
</security:http>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="restaurantauthenticationprovider"/>
<security:authentication-provider ref="userauthenticationprovider"/>
</security:authentication-manager>
<beans:bean id="encoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="11"/>
</beans:bean>
<beans:bean id="restaurantauthenticationprovider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="LoginServiceImpl"/>
<beans:property name="passwordEncoder" ref="encoder"/>
</beans:bean>
<beans:bean id="userauthenticationprovider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="UserLoginServiceImpl"/>
<beans:property name="passwordEncoder" ref="encoder"/>
</beans:bean>
As I have 2 tables to check from which to login, I have 2 DAOAuthenticationProviders.
UserLoginServiceImpl :
#Transactional
#Service("loginuserDetailsService")
public class UserLoginServiceImpl implements UserDetailsService {
#Autowired
private PersonDAO personDAO;
#Autowired
private UserAssembler userAssembler;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException,DataAccessException {
System.out.println("Username is "+username);
Person person = this.personDAO.findPersonByUserName(username.toLowerCase());
if(person == null) { throw new UsernameNotFoundException("Wrong username or password");}
return userAssembler.buildUserFromUserEntity(person);
}
}
Assembler :
#Service("userassembler")
#Transactional
public class UserAssembler {
#Transactional
User buildUserFromUserEntity(Person userEntity){
System.out.println("We are in Userassembler"+userEntity.getEmail());
String username = userEntity.getUsername().toLowerCase();
String password = userEntity.getPassword();
boolean enabled = userEntity.isEnabled();
boolean accountNonExpired = userEntity.isAccountNonExpired();
boolean credentialsNonExpired = userEntity.isCredentialsNonExpired();
boolean accountNonLocked = userEntity.isAccountNonLocked();
Collection<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
return new User(username,password,enabled,accountNonExpired,credentialsNonExpired,accountNonLocked,authorities);
}
}
The above is the config, now I will put the rest code which is failing :
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
Log.d("Username is ", username);
String jsessionid = rest.execute("http://192.168.178.60:8080/j_spring_security_check", HttpMethod.POST,
new RequestCallback() {
#Override
public void doWithRequest(ClientHttpRequest request) throws IOException {
request.getBody().write(("j_username=" + username + "&j_password=" + password).getBytes());
}
}, new ResponseExtractor<String>() {
#Override
public String extractData(ClientHttpResponse response) throws IOException {
List<String> cookies = response.getHeaders().get("Cookie");
if (cookies == null) {
cookies = response.getHeaders().get("Set-Cookie");
}
String cookie = cookies.get(cookies.size() - 1);
System.out.println("Cookie is " + cookie);
// The method below gets me which user is logged in, and I always get null for Controller method.
reply = rest.getForObject(
"http://192.168.178.60:8080/dashboard", String.class);
int start = cookie.indexOf('=');
int end = cookie.indexOf(';');
return cookie.substring(start + 1, end);
}
});
}
});
thread.start();
Update
Finally, the code which worked :
// I am getting the cookie from the server, which I am setting manually for every request, cookie is a static volatile string.
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.add("Cookie", "JSESSIONID=" + StaticRestTemplate.jsessionid);
HttpEntity requestEntity = new HttpEntity(null, requestHeaders);
ResponseEntity rssResponse = rest.exchange(
"http://192.168.178.60:8080/dashboard",
HttpMethod.GET,
requestEntity,
String.class);
String abc = (String) rssResponse.getBody();
Spring's RestTemplate does not keep track of cookies by default. This ensures you don't accidentally pass a cookie (i.e. JSESSIONID) from one user on behalf of another user (i.e. think of using the RestTemplate on a server where many users are leveraging the same RestTemplate).
If you want to do this you can configure it using something like this:
RestTemplate rest = new RestTemplate();
// initialize the RequestFactory to allow cookies
HttpClient httpClient = HttpClientBuilder.create().build();
ClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
rest.setRequestFactory(factory);
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("username", "user");
map.add("password", "password");
String result = rest.postForObject("http://localhost:8080/login", map, String.class);
String hello = rest.postForObject("http://localhost:8080/", map, String.class);
assertThat(hello).isEqualTo("Hello");
To use this code you will need to ensure you have httpclient on your classpath. For example, the following might be in your pom.xml if you are using Maven:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5</version>
</dependency>
Obviously you will need to ensure you include the version of httpclient that works for your dependencies.
Related
I am implementing a custom authentication provider in spring security that authenticates user. Authentication server is in remote side (Restful service). everytime I called my service I encounterd with this error (this code reaches the
return new UsernamePasswordAuthenticationToken(userDetails, authentication.getCredentials().toString(), grantedAuthorities);
part )
{"error":"invalid_client","error_description":"Bad client credentials"}
here is my code:
CustomAuthenticationProvider
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(HttpClients.createDefault());
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(60 * 1000)
.setSocketTimeout(60 * 1000).build();
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
poolingHttpClientConnectionManager.setMaxTotal(20);
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(20);
CloseableHttpClient httpClientBuilder = HttpClientBuilder.create()
.setConnectionManager(poolingHttpClientConnectionManager).setDefaultRequestConfig(requestConfig)
.build();
requestFactory.setHttpClient(httpClientBuilder);
RestTemplate restTemplate = new RestTemplate(requestFactory);
UserInfoRequestBean userInfoRequestBean = new UserInfoRequestBean();
String username = (String)authentication.getPrincipal();
userInfoRequestBean.setUsername(username);
userInfoRequestBean.setPassword((String)authentication.getCredentials());
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<?> httpEntity = new HttpEntity<UserInfoRequestBean>(userInfoRequestBean, headers);
try{
ResponseBean<LoginResponseBean> responseBody = restTemplate.exchange(getLoginUrl(), HttpMethod.POST, httpEntity, new ParameterizedTypeReference<ResponseBean<LoginResponseBean>>() {}).getBody();
UserDetails userDetails = new UserDetails();
userDetails.setGender(responseBody.getResult().getGender());
userDetails.setLastLoginDate(new Date());
userDetails.setYaghutSessionId(responseBody.getResult().getSessionId());
userDetails.setName(responseBody.getResult().getName());
userDetails.setUsername(username);
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
// Granting authorization roles to user
grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_USER"));
return new UsernamePasswordAuthenticationToken(userDetails, authentication.getCredentials().toString(), grantedAuthorities);
}catch (RestClientException exp) {
exp.printStackTrace();
//TODO: implement this method
}
return null;
}
#Override
public boolean supports(Class<?> authentication) {
// TODO Auto-generated method stub
return true;
}
private String getLoginUrl(){
//return restful service url
}
}
Spring-security-config
<bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" />
<bean id="clientCredentialsTokenEndpointFilter"
class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="customAuthenticationManager" />
</bean>
<bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="test/client" />
<property name="typeName" value="Basic" />
</bean>
<bean id="customProvider"
class="com.adpdigital.idm.security.provider.CustomAuthenticationProvider" />
<authentication-manager id="customAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<authentication-provider ref="customProvider" />
</authentication-manager>
<http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="customAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY"/>
<anonymous enabled="false"/>
<http-basic entry-point-ref="clientAuthenticationEntryPoint" />
<!-- include this only if you need to authenticate clients via request parameters -->
<custom-filter ref="clientCredentialsTokenEndpointFilter" after="BASIC_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
Instead of passing UserDetails simply pass the username, when returning UsernamePasswordAuthenticationToken. Below is the example -
return new UsernamePasswordAuthenticationToken(username, authentication.getCredentials().toString(), grantedAuthorities);
I am new with RESTful web services in spring,whenever i am requesting the URL through postman,i am getting random generated token from server side, here you are controller code,through this i am getting random generated token.
#RequestMapping(value = "/api/authenticate", method = RequestMethod.POST)
public #ResponseBody
Result doLogIn(#RequestParam("BulkData") String bulkData, HttpServletResponse response) throws Exception {
ObjectMapper mapper = new ObjectMapper();
JsonNode actualObj = null;
try {
actualObj = mapper.readTree(bulkData);
} catch (IOException e1) {
e1.printStackTrace();
return new Result("Invalid Request", ResultCodes.LOGIN_FAILURE);
}
String userName = actualObj.get("userName").asText();
String password = actualObj.get("password").asText();
logger.debug("[REST]: Attempting login for -> " + userName);
UserDetails details = userDetailService.loadUserByUsername(userName);
// validate password
if (details != null && !details.getPassword().equals(password)) {
logger.debug("[REST]: Invalid username/password");
try {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid username/password");
} catch (IOException e) {
e.printStackTrace();
}
return new Result("Invalid username or password", ResultCodes.LOGIN_FAILURE);
}
// Generate token. ATM, use only username
String generatedToken = Jwts.builder().setSubject(userName)
.setIssuedAt(new Date())
// set token expiration time
.setExpiration(new Date(System.currentTimeMillis() + Config.TOKEN_EXPIRY_PERIOD))
.signWith(SignatureAlgorithm.HS256, servletContext.getInitParameter("API_SECRET_KEY"))
.compact();
// provide token to user in form of a Http Header
response.addHeader(Config.AUTH_TOKEN_HEADER_NAME, generatedToken);
return new Result("Login Success", ResultCodes.LOGIN_SUCCESS_TOKEN_GENERATED);
}
and here is the code for authorization , to do so i am using AuthenticationTokenProcessingFilter,
public class AuthenticationTokenProcessingFilter extends GenericFilterBean {
#Autowired
private UserDetailService userDetailService;
#Autowired
private AuthenticationManager authenticationManager;
private static Logger logger = Logger.getLogger(AuthenticationTokenProcessingFilter.class);
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
// Exclude login URL
if(req.getRequestURI().endsWith("/api/authenticate")) {
chain.doFilter(req, response);
return;
}
// Client must send token in header
String authHeader = req.getHeader(Config.AUTH_TOKEN_HEADER_NAME);
if (authHeader == null) {
logger.error("[REST]: Authentication header was null...");
throw new ServletException("Missing or invalid Authorization header.");
}
// Parse token, fetch user and reload Security Context
try {
String SECRET_KEY = getServletContext().getInitParameter("API_SECRET_KEY");
Jws<Claims> claims = Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(authHeader);
Claims claim = claims.getBody();
String userName = claim.getSubject();
logger.debug("[REST]: Token of user -> " + userName + " expires: " + claim.getExpiration());
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userName, userDetailService.loadUserByUsername(userName).getPassword());
token.setDetails(new WebAuthenticationDetails(req));
Authentication authentication = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (SignatureException e) {
logger.debug("[REST]: Invalid token");
throw new ServletException("Invalid token.");
}
chain.doFilter(req, response);
// clear security context now because we are going for Stateless Web Services
SecurityContextHolder.getContext().setAuthentication(null);
}
now i want to use this generated token to call this method ,
#RequestMapping(value="/api/admin/getEmployeerole", method=RequestMethod.POST)
public List<EmployeeRole> EmployeeRoleList() {
List<EmployeeRole> getRole=employeeRoleService.getAll();
return getRole;
}
now what is happening here when i am writing this URL to postman and adding generated token into header ,and i have used authorization type (No auth), i have also tried with basic Authorization, still my request is going to customAuthenticationEntrypoint and it throws Access denied exception,that the user Role is annonymous, at server side i am getting status 401 unauthorized.it would be great if someone can help to get over from this..
here You are my spring security configuration..
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="hp.bootmgr.authentication.provider" />
<http pattern="/resources/**" security="none" />
<http pattern="/api/**" realm="Protected API" use-expressions="true" auto-config="false" create-session="stateless" entry-point-ref="customAuthenticationEntryPoint">
<!-- <custom-filter position="FORM_LOGIN_FILTER" /> -->
<intercept-url pattern="/api/authenticate" access="permitAll()" />
<intercept-url pattern="/api/admin/**" access="hasRole('ADMIN')" />
<intercept-url pattern="/api/user/**" access="hasAnyRole('ADMIN', 'EMPLOYEE')" />
<intercept-url pattern="/api/member/**" access="hasAnyRole('ADMIN', 'MEMBER')" />
<!--<form-login
login-page="/api/authenticate"
login-processing-url="/j_spring_security_check"
username-parameter="userName"
password-parameter="password" />-->
<logout logout-url="/logout"/>
<csrf disabled="true"/>
</http>
<http auto-config="true" use-expressions="true" entry-point-ref="authenticationEntryPoint">
<access-denied-handler error-page="/403" />
<intercept-url pattern="/login" access="true"/>
<intercept-url pattern="/admin/**" access="hasRole('ADMIN')" />
<!-- Allow access to user pages to admin, as long as there is no more other rules-->
<intercept-url pattern="/user/**" access="hasAnyRole('ADMIN', 'EMPLOYEE')" />
<intercept-url pattern="/member/**" access="hasAnyRole('ADMIN', 'MEMBER')" />
<form-login
login-page="/login"
default-target-url="/home"
authentication-failure-url="/login?failed=1"
login-processing-url="/j_spring_security_check"
username-parameter="userName"
password-parameter="password" />
<logout logout-success-url="/login?logout=1" invalidate-session="true" logout-url="/logout"/>
<!-- enable csrf protection -->
<csrf disabled="true"/>
<session-management>
<concurrency-control max-sessions="1" expired-url="/login" />
</session-management>
</http>
<beans:bean id="customAuthenticationEntryPoint" class="hp.bootmgr.web.services.authentication.CustomAuthenticationEntryPoint" />
<beans:bean id="authenticationTokenProcessingFilter" class="hp.bootmgr.web.services.authentication.AuthenticationTokenProcessingFilter" />
<beans:bean id="authenticationEntryPoint" class="hp.bootmgr.security.AuthenticationEntryPoint">
<beans:constructor-arg name="loginUrl" value="/login"/>
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="userDetailService" />
</authentication-manager>
</beans:beans>
I'm working with Spring Security 3.2 and Hibernate 4. Currently I have a custom login wich works as follows . The URL "/" (root ) is a welcome jsp requests wich ask for a parameter to display a different login according to the same parameter . For example if the user enters the url "/parameter1" (manual action ) , this variable shows me a personalized login generated by a driver that cathes a RequestMapping ( value = " /{parameter}"from there, all URLS will have that parameter , the problem that I have is that when the user wishes to leave or your session expires , spring sends me the url "/" , but I need it to send me a /parameter1 , in order to capture the parameter "parameter1" so that It leaves me in the custom login. That way I would not have to manually re- enter the parameter . My security settings are as follows:
<custom-filter position="FORM_LOGIN_FILTER" ref="myFilter" />
<!-- <form-login login-page="/loginUser" login-processing-url="/testUser/j_spring_security_check"
authentication-failure-url="/loginError" default-target-url="/testUser"
username-parameter="j_username" password-parameter="j_password" /> -->
<logout invalidate-session="true" delete-cookies="JSESSIONID" logout-success-url="/loginUser" logout-url="/testUser/j_spring_security_logout"/>
<session-management invalid-session-url="/" session-fixation-protection="migrateSession" >
<concurrency-control max-sessions="2" expired-url="/" error-if-maximum-exceeded="false"/>
</session-management>
<beans:bean id="loginUrlAuthenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:property name="loginFormUrl" value="/loginUser" />
</beans:bean>
<beans:bean id="myFilter" class="net.universia.test.autenticacionService.LoginAuthenticationFilter">
<beans:property name="authenticationManager" ref='UserauthenticationManager'/>
<beans:property name="authenticationFailureHandler" ref="failureHandler"/>
<beans:property name="authenticationSuccessHandler" ref="successHandler"/>
<beans:property name="filterProcessesUrl" value="/testUser/j_spring_security_check"/>
</beans:bean>
<beans:bean id = "exceptionTranslationFilter" class = "org.springframework.security.web.access.ExceptionTranslationFilter" >
<beans:property name = "authenticationEntryPoint" ref = "loginUrlAuthenticationEntryPoint" />
<beans:property name = "accessDeniedHandler" ref = "accessDeniedHandler" />
</beans:bean>
<beans:bean id="successHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<beans:property name="defaultTargetUrl" value="/testUser"/>
</beans:bean>
<beans:bean id = "accessDeniedHandler" class = "org.springframework.security.web.access.AccessDeniedHandlerImpl" >
<beans:property name = "errorPage" value = "/403" />
</beans:bean>
And the driver that displays the login form is:
#RequestMapping(value ="/{testRef}", method = {RequestMethod.POST,RequestMethod.GET})
public #ResponseBody ModelAndView loginTestRef(#PathVariable("testRef") String testRef,HttpSession session, HttpServletRequest request) {
session.setAttribute("ssidreffh", testRef);
TestDatos test = testService.showTestUserByRef(testRef);
request.getSession().setAttribute("test", test);
ModelAndView mav = new ModelAndView("/loginUser");
mav.addObject("test", test);
return mav;
}
If the user is in the url /dominio/parametro1/paginaPerfil goes or your session ends, spring redirect me to the url "/myApp/parameter1"
and so would be in the login and not the root "/".
I could finally resolve my problem. I implemented a custom filter for logging out with SimpleUrlLogoutSuccessHandler and I could capture the previous URL and from that the parameter that I return with a redirect (/parameter1). This is my code:
public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
#Override
public void onLogoutSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
String testRef = null;
if (authentication != null) {
String refererUrl = request.getHeader("Referer");
System.out.println("variables: " +refererUrl);
String[] parts = refererUrl.split("/");
testRef = parts[5];
}
setDefaultTargetUrl("/"+testRef);
super.onLogoutSuccess(request, response, authentication);
}
}
I've changed the default Authentication Provider for a Custom one.
This is my AuthenticationProvider
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private ParamsProperties paramsProperties;
#SuppressWarnings("unchecked")
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//Check username and passwd
String user = (String) authentication.getPrincipal();
String pass = (String) authentication.getCredentials();
if(StringUtils.isBlank(user) || StringUtils.isBlank(pass) ){
throw new BadCredentialsException("Incorrect username/password");
}
//Create SSO
SingleSignOnService service = new SingleSignOnService(paramsProperties.getServicesServer());
try {
//Check logged
service.setUsername(authentication.getName());
service.setPassword(authentication.getCredentials().toString());
ClientResponse response = service.call();
String result = response.getEntity(String.class);
ObjectMapper mapper = new ObjectMapper();
Map<String,Object> map = mapper.readValue(result, new TypeReference<Map<String,Object>>() {} );
//Read code
String code = (String)map.get("code");
log.debug(" ** [Authenticate] Result: " + code );
for (String s : (List<String>)map.get( "messages" ) ) {
log.debug(" [Authenticate] Message: " + s );
}
if ( code.equals( "SESSION_CREATED" ) || code.equals( "SESSION_UPDATED" ) || code.equals( "SESSION_VERIFIED" ) ) {
UsernamePasswordAuthenticationToken tokenSSO = LoginHelper.getuserSringTokenFromAuthService(map);
return tokenSSO;
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
throw new AuthenticationServiceException( e.getMessage() );
}
}
public boolean supports(Class authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
And this is my security.xml
<http>
<form-login default-target-url ="/Login.html" always-use-default-target="true" login-page="/Login.html" login-processing-url="/j_spring_security_check"
authentication-failure-url="/Login.html" />
<http-basic />
<logout logout-success-url="/Login.html" />
</http>
<beans:bean id="localeFilter" class="com.mycomp.comunes.server.spring.controller.login.MyLocaleFilter" lazy-init="true">
<custom-filter position="LAST"/>
</beans:bean>
<beans:bean id="authenticationProvider" class="com.indra.rfef.comunes.server.spring.manager.autenticacion.CustomAuthenticationProvider">
<custom-authentication-provider />
</beans:bean>
It gets over my CustomAuthenticationProvider, and authenticates correctly the user. But when returning tokenSSO, of type UsernamePasswordAuthenticationToken, it seems it's not saving the user on the Security Context, and when I redirect the user (on the callback of the authenticate) to the index.html, I get redirected back to Login.html.
Why could this happen? I'm I forgetting something?
Please fix your configuration:
<http>
<intercept-url pattern="/Login*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/**" access="ROLE_USER"/>
<form-login login-page="/Login.html" login-processing-url="/j_spring_security_check" authentication-failure-url="/Login.html" />
<http-basic />
<logout logout-success-url="/Login.html" />
</http>
Remove default-target-url ="/Login.html". It makes the redirection after login to the same login page. The default is /.
Add security on all URLs <intercept-url pattern="/**" access="ROLE_USER"/>
Do not remove the anonymous access from the login page
Why you need BasicAuthentication? Remove it if not required: <http-basic />
I'm running in my web app filter, which recieves from external source info about user, if he's logged in or not. Heres my filter:
#Override
public void doFilter( ServletRequest request, ServletResponse response,
FilterChain chain ) throws IOException, ServletException
{
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String loginBean = httpRequest.getHeader( CommonVariables.LOGIN_BEAN );
if ( loginBean == null )
{
System.out.println( "FILTER-----------" );
try
{
String login;
String domain;
//Here i'm getting login and domain string
loginBean = domain + "\\" + login;
httpResponse.addHeader( "LoginBean", loginBean );
System.out.println( login + " " + domain );
} catch ( Exception e )
{
e.printStackTrace();
//redirect to login page
httpResponse.sendRedirect( "..." );
return;
}
}
chain.doFilter( request, response );
}
Not I though that those header will be passed into next filters. Therefore I implemented Spring Security PRE_AUTH_FILTER:
Spring security context
<http use-expressions="true" auto-config="false" entry-point-ref="http403EntryPoint">
<!-- Additional http configuration omitted -->
<custom-filter position="PRE_AUTH_FILTER" ref="siteminderFilter" />
</http>
<beans:bean id="siteminderFilter" class=
"org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
<beans:property name="principalRequestHeader" value="LoginBean"/>
<beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>
<beans:bean id="preauthAuthProvider"
class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<beans:property name="preAuthenticatedUserDetailsService">
<beans:bean id="userDetailsServiceWrapper"
class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<beans:property name="userDetailsService" ref="userDetailsService"/>
</beans:bean>
</beans:property>
</beans:bean>
<beans:bean id="http403EntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint"/>
<beans:bean id="userDetailsService" class="com.execon.security.CustomUserDetailsService"/>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="preauthAuthProvider" />
</authentication-manager>
Then I tried to parse loginBean String in my CustoUserDetailsService and receive actuall user object. But It is not fired, and app fails with this:
org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException: LoginBean header not found in request.
So that means the header is set wrong? Or not set at all? What might be wrong?
Filter setting LoginBean is first one fired, then goes Spring SEcurity. Standard output works ok as I have:
17:12:15,669 INFO [stdout] (http--127.0.0.1-8080-2) FILTER-----------
17:12:15,669 INFO [stdout] (http--127.0.0.1-8080-2) LOGIN DOMAIN
You are setting something in the response and the Spring's class is looking for the same in the request.
The only way you can modify an incoming HttpServletRequest is to decorate it. You should define a class as follows first:
public class AuthHttpServletRequest extends HttpServletRequestWrapper
{
private String loginBean;
public AuthHttpServletRequest(HttpServletRequest aRequest, String loginBean)
{
super(aRequest);
this.loginBean = loginBean;
}
#Override
public String getHeader(String headerName)
{
if(CommonVariables.LOGIN_BEAN.equals(headerName)) {
return this.loginBean;
}
return super.getHeader(headerName);
}
}
Then, replace the following line in your filter:
httpResponse.addHeader( "LoginBean", loginBean );
with this:
request = new AuthHttpServletequest(httpRequest, loginBean);
Then your chain.doFilter gets the request that can return the loginBean as you intended it to, to the Spring's authentication filter class, down in the filter chain.