简体   繁体   中英

Spring security custom login

I am trying to create a login page with three values:

  1. username
  2. password
  3. company name

My application supports different companies, and one single user might be an user of more than one company, so, in the login page, as part of the login, the user must select the company to login.

This is what I am currently using:

  1. Java 8
  2. Spring 4.0.3.RELEASE (I am using xmls files)
  3. Spring security 4.0.3.RELEASE
  4. Hibernate 4.2.21.Final
  5. Thymeleaf 2.1.4.RELEASE

This is my spring-security.xml

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


    <b:bean id="passwordEncoder"
            class="org.springframework.security.authentication.encoding.ShaPasswordEncoder">
        <b:constructor-arg value="256"/>
        <b:property name="iterations" value="8224"/>
    </b:bean>

    <!-- Define the User property to use for salting password encoding -->
    <b:bean id="saltSource"
            class="org.springframework.security.authentication.dao.ReflectionSaltSource">
        <b:property name="userPropertyToUse" value="userSalt"/>
    </b:bean>

    <b:bean id="userCompanyAuthenticationProvider"
            class="com.mycompany.security.authentication.UserCompanyAuthenticationProvider">

    </b:bean>

    <!--Authentication provider to use for Spring Security-->
    <!--<b:bean id="daoAuthenticationProvider" -->
    <!--class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">-->
    <!--&lt;!&ndash; userDetailsService is annotated at com.mycompany.security.userdetails.UserDetailsServiceImpl &ndash;&gt;-->
    <!--<b:property name="userDetailsService" ref="userDetailsService"/>-->
    <!--<b:property name="passwordEncoder" ref="passwordEncoder"/>-->
    <!--<b:property name="saltSource" ref="saltSource"/>-->
    <!--</b:bean>-->

    <b:bean id="authenticationEntryPoint"
            class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint"/>

    <!--<authentication-manager alias="authenticationManager">-->
    <!--<authentication-provider ref="daoAuthenticationProvider" />-->
    <!--</authentication-manager>-->


    <!-- Turn off Spring Security for the following URL/patterns -->

    <http pattern="/_ui/**" security="none"/>
    <http pattern="/resources/**" security="none"/>

    <!-- Configure the HTTP realm/security settings and enable SpEL expressions -->
    <http use-expressions="true" auto-config="false" entry-point-ref="loginEntryPoint">

        <custom-filter ref="userCompanyFormLoginFilter"
                       position="FORM_LOGIN_FILTER"/>
        <csrf disabled="true"/>
        <!-- Enable remember me cookie functionality -->
        <!--<remember-me key="myappRememberMe"-->
                     <!--token-validity-seconds="2419200"/>-->

        <intercept-url pattern="/favicon.ico" access="permitAll"/>
        <intercept-url pattern="/login" access="permitAll"/>
        <intercept-url pattern="/logout" access="permitAll"/>
        <intercept-url pattern="/auth/**" access="permitAll"/>
        <intercept-url pattern="/signup/**" access="permitAll"/>
        <intercept-url pattern="/static/**" access="permitAll"/>
        <intercept-url pattern="/resources/**" access="permitAll"/>
        <intercept-url pattern="/_ui/**" access="permitAll"/>

        <intercept-url pattern="/user" access="hasRole('ROLE_USER')"/>

        <intercept-url pattern="/admin" access="hasRole('ROLE_ADMIN')"/>

        <intercept-url pattern="/**" access="isAuthenticated()"/>

        <access-denied-handler ref="customAccessDeniedHandler"/>

        <!-- Login Page -->
        <!--<form-login login-page="/login" default-target-url="/"-->
        <!--login-processing-url="/static/j_spring_security_check"-->
        <!--authentication-failure-url="/login?error=true" />-->

        <!-- URL for logging out and specific cookies to delete when doing so. -->
        <!--<logout logout-url="/logout" delete-cookies="JSESSIONID"/>-->
        <!-- delete-cookies="JSESSIONID, SPRING_SECURITY_REMEMBER_ME_COOKIE" -->

        <session-management>
            <concurrency-control max-sessions="1"
                                 error-if-maximum-exceeded="false"/>
        </session-management>
    </http>

    <authentication-manager alias="authenticationManager">
        <authentication-provider ref="userCompanyAuthenticationProvider"/>
    </authentication-manager>

    <b:bean id="userCompanyFormLoginFilter" class="com.mycompany.security.filter.UserCompanyAuthenticationFilter">
        <b:property name="filterProcessesUrl" value="/login/form"/>
        <b:property name="authenticationManager" ref="authenticationManager"/>
        <b:property name="usernameParameter" value="username"/>
        <b:property name="passwordParameter" value="password"/>
    </b:bean>

    <b:bean id="loginEntryPoint"
            class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
        <b:constructor-arg value="/login"/>
    </b:bean>

    <b:bean id="customAccessDeniedHandler" class="com.mycompany.security.AccessDeniedHandlerApp"/>
</b:beans>

This is my UserCompanyAuthenticationFilter

package com.mycompany.security.filter;
import com.mycompany.security.authentication.UserCompanyAuthenticationToken;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class UserCompanyAuthenticationFilter extends UsernamePasswordAuthenticationFilter
{

    @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException
    {
        if (!request.getMethod().equals("POST"))
        {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }

        String username = obtainUsername(request);
        String password = obtainPassword(request);
        String company = request.getParameter("company");

        UserCompanyAuthenticationToken authRequest = new UserCompanyAuthenticationToken(username, password, company);

        setDetails(request, authRequest);
        return this.getAuthenticationManager().authenticate(authRequest);
    }

}

This is my UserCompanyAuthenticationProvider:

package com.mycompany.security.authentication;
import com.mycompany.entity.User;
import com.mycompany.myapp.dao.service.CompanyService;
import com.mycompany.myapp.dao.service.LoginService;
import com.mycompany.security.RoleEnumerator;
import com.mycompany.security.UnknownRoleException;
import org.apache.log4j.Logger;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

import javax.inject.Inject;
import java.util.ArrayList;
import java.util.Collection;

public class UserCompanyAuthenticationProvider implements AuthenticationProvider
{

    private LoginService loginService;

    private CompanyService companyService;

    private static final Logger LOGGER = Logger.getLogger(UserCompanyAuthenticationProvider.class);

    @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException
    {
        UserCompanyAuthenticationToken token = (UserCompanyAuthenticationToken) authentication;
        String username = token.getName();
        String company = token.getCompany();

        User user = null;
        if (username != null)
        {
            user = loginService.getByUsername(username, companyService.get(Long.parseLong(company)));
        }

        if (user == null)
        {
            throw new BadCredentialsException("Invalid username - password");
        }

        String password = user.getPasswordHash();

        if (!loginService.isValidUsernameAndPasswordHashCombination(username, password))
        {
            throw new BadCredentialsException("Invalid username - password");
        }

        Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
        try
        {
            RoleEnumerator.getApplicableRoles(user)
                    .forEach(role -> authorities.add(new SimpleGrantedAuthority(role.toString())));

        }
        catch (UnknownRoleException rex)
        {
            LOGGER.error(rex.getMessage(), rex);
        }

        return new UserCompanyAuthenticationToken(user.getUsername(), password, authorities, company);

    }

    @Override public boolean supports(Class<?> authentication)
    {
        return UserCompanyAuthenticationToken.class.equals(authentication);
    }

    @Inject public void setLoginService(LoginService loginService)
    {
        this.loginService = loginService;
    }

    @Inject public void setCompanyService(CompanyService companyService)
    {
        this.companyService = companyService;
    }

}

And this is my UserCompanyAuthenticationToken

package com.mycompany.security.authentication;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;

import java.util.Collection;

public class UserCompanyAuthenticationToken extends UsernamePasswordAuthenticationToken
{
    private final String company;

    public UserCompanyAuthenticationToken(String principal, String credentials, String company)
    {
        super(principal, credentials);
        this.company = company;
    }

    public UserCompanyAuthenticationToken(String principal, String credentials,
            Collection<? extends GrantedAuthority> authorities, String company)
    {
        super(principal, credentials, authorities);
        this.company = company;
    }

    public String getCompany()
    {
        return company;
    }
}

So far so good, I am sending three values in the httprequest (username, password and company) and everything is working properly.

However, the password is not encoded, what I want to do is to encode the password and sent the saltSource in order to compare the password and salt.

Does anybody has an example about how to do this?, or does anybody has any suggestions or directions about how to do it?

thanks in advance

I have made the following change (for now because I will use BCryptPasswordEncoder as @szymon recommended)

This is my security.xml:

 <b:bean id="passwordEncoder"
            class="org.springframework.security.authentication.encoding.ShaPasswordEncoder">
        <b:constructor-arg value="256"/>
        <b:property name="iterations" value="8224"/>
    </b:bean>

   <b:bean id="userCompanyAuthenticationProvider"
            class="com.digitalkresko.security.authentication.UserCompanyAuthenticationProvider">
        <b:property name="passwordEncoder" ref="passwordEncoder"/>
    </b:bean>

Then, I made the following change in my UserCompanyAuthenticationProvider:

public class UserCompanyAuthenticationProvider implements AuthenticationProvider
{

    private LoginService loginService;

    private CompanyService companyService;

    **private ShaPasswordEncoder passwordEncoder;**

    private static final Logger LOGGER = Logger.getLogger(UserCompanyAuthenticationProvider.class);

    @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException
    {
        UserCompanyAuthenticationToken token = (UserCompanyAuthenticationToken) authentication;
        String username = token.getName();
        String company = token.getCompany();

        User user = null;
        if (username != null)
        {
            user = loginService.getByUsername(username, companyService.get(Long.parseLong(company)));
        }

        if (user == null)
        {
            throw new BadCredentialsException("Invalid username - password");
        }

        String password = passwordEncoder.encodePassword(token.getCredentials().toString(), user.getUserSalt());

        if (!loginService.isValidUsernameAndPasswordHashCombination(username, password))
        {
            throw new BadCredentialsException("Invalid username - password");
        }

        Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
        try
        {
            RoleEnumerator.getApplicableRoles(user)
                    .forEach(role -> authorities.add(new SimpleGrantedAuthority(role.toString())));

        }
        catch (UnknownRoleException rex)
        {
            LOGGER.error(rex.getMessage(), rex);
        }

        return new UserCompanyAuthenticationToken(user.getUsername(), password, authorities, company);

    }

However, I am not sure if this is the correct way to validate the username/password. What do you think?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM