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());
}
}
}
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.