0

I’m trying to use spring boot security in my application. I need to give access to both sales men and customers. Each are mapped to different entities and in turn use different repositories.

How can my UserDetailServive implementation use a different repository depending on a custom form parameter?

<form th:action="@{/login}" method="post">
    <div>
        <label>User Name: <input type="text" name="username"/></label>
        <label>Password: <input type="password" name="password"/></label>
        <label>User type: <input type="radio" name="userType" value="customer"/>
                          <input type="radio" name="userType" value="salesMen"/></label>
    </div>
    <div><input type="submit" value="Login"/></div>
</form>

@Service
public class UserDetailServiceImpl implements UserDetailsService {

    @Autowired
    SalesMenRepository salesMenRepository;

    @Autowired
    CustomersRepository customersRepository;    


    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {

        // How can I get userType parameter ????

        if ("salesMen".equals(userType)) {
            Optional<SalesMan> salesMan = salesMenRepository.findById(userName);
            if (!salesMan.isPresent()) {
                throw new UsernameNotFoundException(userName);
            }

            return new UserDetailsImp(salesMan.get());
        } else {
            Optional<Customer> customer = customersRepository.findById(userName);
            if (!customer.isPresent()) {
                throw new UsernameNotFoundException(userName);
            }

            return new UserDetailsImp(customer.get());      
        }
    }

}   
ThanhLD
  • 634
  • 5
  • 20
Roberto Petrilli
  • 711
  • 1
  • 8
  • 22

1 Answers1

0

You can join userName with userType by any character, eg colon: userName:userType, and in loadUserByUsername method, you split and get it String[] parts = userName.split(":");
But when you join custom parameter into userName, you must custom authentication filter. In my case, I add new custom param have name is dmBhxhId. I create CustomUser:

public class CustomUser extends User {
    private Long dmBhxhId;

    public Long getDmBhxhId() {
        return dmBhxhId;
    }

    public void setDmBhxhId(Long dmBhxhId) {
        this.dmBhxhId = dmBhxhId;
    }

    public CustomUser(String username, String password, Collection<? extends GrantedAuthority> authorities,
            Long dmBhxhId) {
        super(username, password, authorities);
        this.dmBhxhId = dmBhxhId;
    }

    public CustomUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
        super(username, password, authorities);
    }

}

And I custom authentication filter

public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    private String extraParameter = "extra";
    private String delimiter = ":";

    /**
     * Given an {@link HttpServletRequest}, this method extracts the username
     * and the extra input values and returns a combined username string of
     * those values separated by the delimiter string.
     *
     * @param request
     *            The {@link HttpServletRequest} containing the HTTP request
     *            variables from which the username client domain values can be
     *            extracted
     */
    @Override
    protected String obtainUsername(HttpServletRequest request) {
        String username = request.getParameter(getUsernameParameter());
        String extraInput = request.getParameter(getExtraParameter());
        Map<String, String[]> map = request.getParameterMap();
        String combinedUsername = username + getDelimiter() + extraInput;
        return combinedUsername;
    }

    /**
     * @return The parameter name which will be used to obtain the extra input
     *         from the login request
     */
    public String getExtraParameter() {
        return this.extraParameter;
    }

    /**
     * @param extraParameter
     *            The parameter name which will be used to obtain the extra
     *            input from the login request
     */
    public void setExtraParameter(String extraParameter) {
        this.extraParameter = extraParameter;
    }

    /**
     * @return The delimiter string used to separate the username and extra
     *         input values in the string returned by
     *         <code>obtainUsername()</code>
     */
    public String getDelimiter() {
        return this.delimiter;
    }

    /**
     * @param delimiter
     *            The delimiter string used to separate the username and extra
     *            input values in the string returned by
     *            <code>obtainUsername()</code>
     */
    public void setDelimiter(String delimiter) {
        this.delimiter = delimiter;
    }
}

In SecurityConfiguration file, I init CustomAuthenticationFilter

 @Bean  
    public CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
        CustomAuthenticationFilter bcsAuthFilter = new CustomAuthenticationFilter();
        bcsAuthFilter.setAuthenticationManager(authenticationManager());
        bcsAuthFilter.setAuthenticationFailureHandler(ajaxAuthenticationFailureHandler);
        bcsAuthFilter.setAuthenticationSuccessHandler(ajaxAuthenticationSuccessHandler);
        bcsAuthFilter.setFilterProcessesUrl("/api/authentication");
        bcsAuthFilter.setPostOnly(true);
        bcsAuthFilter.setExtraParameter("dm_bhxh_id");
        bcsAuthFilter.setUsernameParameter("j_username");
        bcsAuthFilter.setPasswordParameter("j_password");
        return bcsAuthFilter;
    } 

And call it in configure method

.addFilterBefore(bcsAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)

look like

    @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .csrf()
                .ignoringAntMatchers("/websocket/**").ignoringAntMatchers("/api/public/odts/**")
            .and()
                .addFilterBefore(bcsAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .addFilterAfter(new CsrfCookieGeneratorFilter(), CsrfFilter.class)
                .exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint)
....

Done, hope to help you! Sorry, my english is not good.

ThanhLD
  • 634
  • 5
  • 20
  • Thanks, I think this work around can do. If I’m not wrong you filter runs before standard spring authentication processing, reads the form parameters and puts them into username by overwriting method obtainUsername Of UsernamePasswordAuthenticationFilter. One possible alternative implementation of the same concept might be using a front-end approach like pre-processing the form in jquery and join 2 fields into one. – Roberto Petrilli Apr 14 '18 at 08:31