Peter Minearo
Peter Minearo

Reputation: 338

Spring Security not using custom UserDetaisService

I am using Spring Security 4.2.5 and I am trying to set up a simple Login page using 'username' and 'password' to have a user login. Right now, I am not worried about whether it comes in via http, or https. I will change that setting later on. What I am trying to do is to get Spring Security to see the username/password, call my UserDetailsService class, authenticate the request, and then redirect the browser to 'index.html'. The problem I am having is I keep getting access denied. I turned on debug logging and noticed that my custom UserDetalsService is not being called to get the User data. What is missing to get Spring Security to call AdminUserService? I can provide logging statements if need be.

<body>

    <div>
        <div>
            <h2>Login</h2>
        </div>
    </div>

    <form action="/admin/login" method="post">
        Login:<br>
        <input type="text" name="username">
        <br>
        Password:<br>
        <input type="text" name="password">
        <br><br>
        <input type="submit" value="Submit">
    </form>

</body>

My applicationContext.xml file looks like

<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">

<import resource="classpath:my-admin-context.xml" />

<bean id="adminUserService" class="....AdminUserService">
    <property name="userMapper" ref="userMapper" />
    <property name="userRoleMapper" ref="userRoleMapper" />
</bean>

<bean id="adminAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler">
    <property name="defaultTargetUrl" value="/index.html" />
    <property name="alwaysUseDefaultTargetUrl" value="true" />
    <property name="useReferer" value="true" />
</bean>

<security:http>

    <security:intercept-url pattern="/login.html*" access="IS_AUTHENTICATED_ANONYMOUSLY" requires-channel="any" />

    <security:intercept-url pattern="/**" access="ROLE_ADMIN" requires-channel="any" />

    <security:form-login login-page="/login.html" authentication-success-handler-ref="adminAuthenticationSuccessHandler" />
    <security:logout logout-url="/logout" invalidate-session="true" delete-cookies="JSESSIONID" logout-success-url="/login.html" />

</security:http>

<security:authentication-manager>
    <security:authentication-provider user-service-ref="adminUserService">
        <security:password-encoder hash="sha" />
    </security:authentication-provider>
</security:authentication-manager>

The AdminUserSerivce looks like

public class AdminUserService implements UserDetailsService {

private final Logger logger = LoggerFactory.getLogger(getClass());

private UserMapper userMapper;

private UserRoleMapper userRoleMapper;

public UserMapper getUserMapper() {

    return userMapper;
}

@Required
public void setUserMapper(final UserMapper userMapper) {

    this.userMapper = userMapper;
}

public UserRoleMapper getUserRoleMapper() {

    return userRoleMapper;
}

@Required
public void setUserRoleMapper(final UserRoleMapper userRoleMapper) {

    this.userRoleMapper = userRoleMapper;
}

@Override
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {

    logger.debug("Looking up User: {}", username);

    final UserDo userDo = userMapper.findUserByLogin(username);
    User user = null;

    if (userDo == null) {
        logger.warn("User does not exist: {}", username);

        user = new User(username, null, false, false, false, false, new ArrayList<GrantedAuthority>());
    } else if (StringUtils.equals(userDo.getStatus(), EtpAdminConstants.INACTIVE)) {
        logger.warn("User is Inactive: {}", username);
        logger.info("User DO: {}", userDo);

        user = new User(username, null, false, false, false, false, new ArrayList<GrantedAuthority>());
    } else {
        final List<GrantedAuthority> authorities = getAuthorities(userDo);

        logger.debug("Granted Authorities: {}", authorities);

        user = new User(username, userDo.getPassword(), authorities);
    }

    return user;
}

List<GrantedAuthority> getAuthorities(final UserDo userDo) {

    final List<UserRole> userRoleList = userRoleMapper.findRoleMapping(userDo.getId());

    logger.debug("Found Roles: {}", userRoleList);

    final List<GrantedAuthority> authorities = new ArrayList<>();

    userRoleList.forEach(userRole -> authorities.add(new SimpleGrantedAuthority(userRole.getRoleName())));

    return authorities;
}

}

Here is the Logging information

[o.a.c.a.AuthenticatorBase] Security checking request POST /admin/login
[o.a.c.a.AuthenticatorBase] Not subject to any constraint
[o.s.s.w.u.m.AntPathRequestMatcher] Checking match of request : '/login'; against '/login.htm*'
[o.s.s.w.FilterChainProxy] /login at position 1 of 11 in additional filter chain; firing Filter: 'ChannelProcessingFilter'
[o.s.s.w.u.m.AntPathRequestMatcher] Request '/login' matched by universal pattern '/**'
[o.s.s.w.a.c.ChannelProcessingFilter] Request: FilterInvocation: URL: /login; ConfigAttributes: [ANY_CHANNEL]
[o.s.s.w.FilterChainProxy] /login at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
[o.s.s.w.c.HttpSessionSecurityContextRepository] HttpSession returned null object for SPRING_SECURITY_CONTEXT
[o.s.s.w.c.HttpSessionSecurityContextRepository] No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@7a1cf085. A new one will be created.
[o.s.s.w.FilterChainProxy] /login at position 3 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
[o.s.s.w.FilterChainProxy] /login at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
[o.s.s.w.FilterChainProxy] /login at position 5 of 11 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
[o.s.s.w.FilterChainProxy] /login at position 6 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
[o.s.s.w.s.DefaultSavedRequest] pathInfo: both null (property equals)
[o.s.s.w.s.DefaultSavedRequest] queryString: both null (property equals)
[o.s.s.w.s.DefaultSavedRequest] requestURI: arg1=/admin/login; arg2=/admin/login (property equals)
[o.s.s.w.s.DefaultSavedRequest] serverPort: arg1=8080; arg2=8080 (property equals)
[o.s.s.w.s.DefaultSavedRequest] requestURL: arg1=http://localhost:8080/admin/login; arg2=http://localhost:8080/admin/login (property equals)
[o.s.s.w.s.DefaultSavedRequest] scheme: arg1=http; arg2=http (property equals)
[o.s.s.w.s.DefaultSavedRequest] serverName: arg1=localhost; arg2=localhost (property equals)
[o.s.s.w.s.DefaultSavedRequest] contextPath: arg1=/admin; arg2=/admin (property equals)
[o.s.s.w.s.DefaultSavedRequest] servletPath: arg1=/login; arg2=/login (property equals)
[o.s.s.w.s.HttpSessionRequestCache] [http-nio-8080-exec-5]: Removing DefaultSavedRequest from session if present
[o.s.s.w.FilterChainProxy] /login at position 7 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
[o.s.s.w.FilterChainProxy] /login at position 8 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
[o.s.s.w.a.AnonymousAuthenticationFilter] Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@90576bf4: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@21a2c: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: FBB14A73FFEA49FF371DE7E74DBCBF31; Granted Authorities: ROLE_ANONYMOUS'
[o.s.s.w.FilterChainProxy] /login at position 9 of 11 in additional filter chain; firing Filter: 'SessionManagementFilter'
[o.s.s.w.FilterChainProxy] /login at position 10 of 11 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
[o.s.s.w.FilterChainProxy] /login at position 11 of 11 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
[o.s.s.w.a.i.FilterSecurityInterceptor] Secure object: FilterInvocation: URL: /login; Attributes: [ROLE_ADMIN]
[o.s.s.w.a.i.FilterSecurityInterceptor] Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@90576bf4: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@21a2c: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: FBB14A73FFEA49FF371DE7E74DBCBF31; Granted Authorities: ROLE_ANONYMOUS
[o.s.s.a.v.AffirmativeBased] Voter: org.springframework.security.access.vote.RoleVoter@5b444231, returned: -1
[o.s.s.a.v.AffirmativeBased] Voter: org.springframework.security.access.vote.AuthenticatedVoter@52a53135, returned: 0
[o.s.b.f.s.DefaultListableBeanFactory] Returning cached instance of singleton bean 'sqlSessionFactory'
[o.s.s.w.a.ExceptionTranslationFilter] Access is denied (user is anonymous); redirecting to authentication entry point
[o.s.s.w.s.HttpSessionRequestCache] DefaultSavedRequest added to Session: DefaultSavedRequest[http://localhost:8080/admin/login]
[o.s.s.w.a.ExceptionTranslationFilter] Calling Authentication entry point.
[o.s.s.w.DefaultRedirectStrategy] Redirecting to 'http://localhost:8080/admin/login.html'
[o.s.s.w.c.HttpSessionSecurityContextRepository] SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
[o.s.s.w.c.SecurityContextPersistenceFilter] SecurityContextHolder now cleared, as request processing completed

Upvotes: 0

Views: 293

Answers (2)

Peter Minearo
Peter Minearo

Reputation: 338

I finally figured it out. @vikas, that little bit of information got me in the right direction. Thanks! I had the wrong configuration. At the bottom of this answer is the configuration I had to go with. You will notice a couple of things:

  1. Create a separate <http> for the login.html page.
  2. You must specify the following parameters on the <form-login>
    • login-processing-url
    • username-parameter
    • password-parameter

Spring Security will not find '/login'. You must specify this parameter, contrary to what the documentation says.

"The login form simply contains username and password input fields, and posts to the URL that is monitored by the filter (by default this is /login)." -- Form Login Filter

Also, Spring Security will not find the default 'username' and 'password, contrary to the documentation.

"Login forms must present two parameters to this filter: a username and password. The default parameter names to use are contained in the static fields SPRING_SECURITY_FORM_USERNAME_KEY and SPRING_SECURITY_FORM_PASSWORD_KEY. The parameter names can also be changed by setting the usernameParameter and passwordParameter properties." -- UsernamePasswordAuthenticationFilter

Code snippet is from the UsernamePasswordAuthenticationFilter class.

public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";

You must specify these 3 parameters in your configuration.

<security:http pattern="/login.htm*" security="none" />

    <security:http>

        <security:intercept-url pattern="/**" access="ROLE_ADMIN" requires-channel="any" />

        <security:form-login login-page="/login.html" login-processing-url="/login" authentication-success-handler-ref="adminAuthenticationSuccessHandler" username-parameter="username" password-parameter="password"/>

        <security:logout logout-url="/logout" invalidate-session="true" delete-cookies="JSESSIONID" logout-success-url="/login.html" />

    </security:http>

Upvotes: 1

vikas
vikas

Reputation: 280

You are securing all request using below line

<security:intercept-url pattern="/**" access="ROLE_ADMIN" requires-channel="any" />

So Please permit all user to access login request. "/Login.html*" is not pattern.

Upvotes: 0

Related Questions