Spring-security/Grails app - Custom WebSecurity configurations not getting called - authentication

My project is based on Grail 2.5.6 and Spring plugins. I'm trying to create a custom auth provider, filter and token extending their respective basic classes.
this.getAuthenticationManager().authenticate(authRequest)
In my filter the authentication manager is always null. So, it throws cannot invoke authenticate() on a null object. When I debug on the authenticationManager, it lists other provider names but my custom one.
Here is my custom web security config
#Configuration
#EnableGlobalMethodSecurity(securedEnabled=true)
public class CustomWebSecurityConfig extends WebSecurityConfigurerAdapter {
OrbisAuthenticationProvider orbisAuthenticationProvider
public CustomWebSecurityConfig() {
super()
log.debug "configure custom security"
print("configure custom security")
}
#Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
print("configure method 1")
log.debug "configure method 1"
auth.authenticationProvider(orbisAuthenticationProvider)
}
#Bean(name= BeanIds.AUTHENTICATION_MANAGER)
#Override
AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean()
}
#Bean
OrbisAuthenticationFilter orbisAuthenticationProvider() throws Exception {
log.debug "orbis Authentication provider"
OrbisAuthenticationProvider orbisAuthenticationProvider = new OrbisAuthenticationProvider(authenticationManagerBean())
return orbisAuthenticationProvider
}
#Bean
#Autowired
public OrbisAuthenticationFilter orbisAuthenticationFilter() throws Exception {
print("configure orbis filtr")
OrbisAuthenticationFilter oaf = new OrbisAuthenticationFilter()
oaf.setAuthenticationManager(authenticationManagerBean())
oaf.setFilterProcessesUrl("j_orbis_security_check")
oaf.setUsernameParameter("email")
oaf.setPasswordParameter("password")
oaf.setAuthenticationSuccessHandler(new SavedRequestAwareAuthenticationSuccessHandler()
.setDefaultTargetUrl("/oauth/authorize"))
oaf.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler()
.setDefaultFailureUrl("/loginWithOrbis"))
oaf.afterPropertiesSet()
return oaf
}
}
On debugging, it doesn't look like any of these methods are getting called. The annotations don't seem enough to get picked up. I had tried #ComponentScan too.
Do I have to inject this security config somewhere? How do I get authManager to be available in my filter?
OrbisAuthFilter
class OrbisAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
// #Autowired
OrbisAuthenticationProvider orbisAuthenticationProvider
OrbisAuthenticationFilter() {
super("/j_orbis_security_check")
orbisAuthenticationProvider = new OrbisAuthenticationProvider()
}
void afterPropertiesSet() {
assert authenticationManager != null, 'authenticationManager must be specified'
}
#Override
Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String username = request.getParameter("email")
String password = request.getParameter("password")
String accessCode = request.getParameter("accessCode")
OrbisAuthenticationToken authRequestForAuthentication = new OrbisAuthenticationToken(username, password, accessCode)
// This throws error because getAuthenticationManager returns null
// authRequestForAuthentication = this.getAuthenticationManager.authenticate(authRequestForAuthentication)
//This works if I instantiate the orbis provider object in the constructor
authRequestForAuthentication = this.orbisAuthenticationProvider.authenticate(authRequestForAuthentication)
SecurityContextHolder.getContext().setAuthentication(authRequestForAuthentication)
return authRequestForAuthentication
}
protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
}
#Override
#Autowired
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
}
OrbisAuthProvider
class OrbisAuthenticationProvider implements AuthenticationProvider {
#Override
Authentication authenticate(Authentication authentication) throws AuthenticationException {
OrbisAuthenticationToken orbisAuth = (OrbisAuthenticationToken) authentication
String username = orbisAuth.principal
String password = orbisAuth.credentials
String orbisAccessCode = orbisAuth.orbisAccessCode
def urlToUse = 'https://coopstatus.neu.edu/sail_api/full.aspx?' + 'ac=' + orbisAccessCode + '&e='+ username + '&p=' + password
HttpClient httpClient = DefaultHttpClient.newInstance()
HttpGet getRequest = new HttpGet(urlToUse)
HttpResponse httpResponse = httpClient.execute(getRequest)
JSONObject orbisResponse = new JSONObject(httpResponse.getEntity().getContent().getText())
// if(orbisResponse.get("IsFound")) {
// //Return error not authenticated
// }
Collection<GrantedAuthority> orbisUserGrantedAuthorities = getLDAPUserAuthorities(orbisResponse.get("Email"))
orbisAuth = new OrbisAuthenticationToken(username, password, orbisAccessCode, orbisUserGrantedAuthorities)
return orbisAuth
}
private Collection<GrantedAuthority> getLDAPUserAuthorities(String username) {
LDAPUserDetails currentLdapUserDetails
try {
currentLdapUserDetails = new LDAPUserDetailsService().loadUserByOrbisUsername(username)
log.debug currentLdapUserDetails
} catch(org.springframework.security.core.userdetails.UsernameNotFoundException e) {
log.error("User " + username + " not found in ldap", e)
}
Collection<GrantedAuthority> authorities = new ArrayList<>()
for (String authority : currentLdapUserDetails.authorities) {
authorities.add(new SimpleGrantedAuthority(authority))
}
return authorities
}
#Override
public boolean supports(Class<?> authentication) {
return (OrbisAuthenticationToken.class
.isAssignableFrom(authentication));
}
}
Resources.groovy
import edu.neu.security.OrbisAuthenticationFilter
import edu.neu.security.OrbisAuthenticationProvider
beans = {
userDetailsService(edu.neu.security.LDAPUserDetailsService)
orbisAuthenticationProvider(OrbisAuthenticationProvider)
orbisAuthenticationFilter(OrbisAuthenticationFilter) {
orbisAuthenticationProvider = ref("orbisAuthenticationProvider")
requiresAuthenticationRequestMatcher = ref('filterProcessUrlRequestMatcher')
// This throws error during startup. Unable to init bean
// authenicationManager = ref("authenicationManager")
}
myOAuth2ProviderFilter(OAuth2ProviderFilters) {
//grailsApplication = ref('grailsApplication')
// properties
}
}
I followed some of the concepts from this project: https://github.com/ppazos/cabolabs-ehrserver/
Even if the whole process is executed and securityContext is set with authenticated, when I hit oauth/authorize to get Authorization_Code, it redirects back to '/login/auth'. It still doesn't know that a user is already authenticated.

When you add an authentication provider to the AuthenticationManagerBuilder bean (which comes from AuthenticationConfiguration), the authentication manager bean you declare is not used.
Try:
#Configuration
#EnableGlobalMethodSecurity(securedEnabled=true)
public class CustomWebSecurityConfig {
OrbisAuthenticationProvider lwoAuthProvider;
public CustomWebSecurityConfig() {
//
}
#Bean(name= BeanIds.AUTHENTICATION_MANAGER)
AuthenticationManager authenticationManagerBean() throws Exception {
return new ProviderManager(Arrays.asList(lwoAuthProvider));
}
Your AuthenticationManager bean should get picked up and will be used for method security. You can also #Autowire it in your filter if it is being managed by Spring, or #Autowire it in the #Configuration class that instantiates your filter.
NOTE: the above class WILL NOT create any of the Spring Security filters.
(The filter chain wasn't being created anyway - you didn't annotate your class with #EnableWebSecurity)

Related

Using Basic auth with OAuth2 and JHipster

we use jhipster to generate gateway and microservices with OAuth2 authentication, and that works fine with a JHipster Registry and a Keycloak server. But we have a microservice that will be called from an external service, and this service use basic authentication.
So, on the gateway, we need to send login and pasword from basic auth to keycloak server, and use access token to call our service. I get access token by adding filter in MicroserviceSecurityConfiguration class :
http.addFilterBefore(basicAuthFilter, UsernamePasswordAuthenticationFilter.class);
And here a extract of filter method :
ResourceOwnerPasswordResourceDetails details = new ResourceOwnerPasswordResourceDetails();
details.setAccessTokenUri("http://keycloakserver/auth/realms/jhipster/protocol/openid-connect/token");
details.setGrantType("password");
details.setClientId("clientId");
details.setClientAuthenticationScheme(AuthenticationScheme.form);
details.setUsername(login);
details.setPassword(password);
AccessTokenRequest tokenRequest = new DefaultAccessTokenRequest();
ResourceOwnerPasswordAccessTokenProvider provider = new ResourceOwnerPasswordAccessTokenProvider();
OAuth2AccessToken accessToken = provider.obtainAccessToken(details, tokenRequest);
I guess I have to store this token in tokenStore, but I don't know how. So my questions are how use this token, and does the the way I get it is correct ?
Thanks for your help !
I Have same problem with you in my production system, then I changed Authentication server to JHipster UAA server, and problem is resolved.
I think you are using first kind now:
OAuth 2.0 / OIDC Authentication:
this uses an OpenID Connect server, like Keycloak or Okta, which handles authentication outside of the application.
Authentication with JHipster UAA server:
this uses a JHipster UAA server that must be generated separately, and which is an OAuth2 server that handles authentication outside of the application.
After some tests and trial and error, I managed to do that I want.
First, I created a BasicAuthenticationFilter class :
#Configuration
public class BasicAuthenticationFilter implements Filter {
private static final String GRANT_TYPE = "password";
private static final String BASIC_AUTH_HEADER = "Authorization";
private static final String BASIC_PREFIX = "Basic ";
#Value("${security.oauth2.client.access-token-uri}")
private String accessTokenUri;
#Value("${security.oauth2.client.client-id}")
private String clientId;
#Value("${security.oauth2.client.client-secret}")
private String clientSecret;
#Autowired
private TokenStore tokenStore;
private ResourceOwnerPasswordAccessTokenProvider provider;
public BasicAuthenticationFilter( ) {
provider = new ResourceOwnerPasswordAccessTokenProvider();
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String header = null;
if (request instanceof HttpServletRequest) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
header = httpRequest.getHeader(BASIC_AUTH_HEADER);
if (header != null && header.startsWith(BASIC_PREFIX)) {
String base64 = header.substring(BASIC_PREFIX.length());
String loginPassword = new String(Base64.getDecoder().decode(base64.getBytes()));
String[] split = loginPassword.split(":");
String login = split[0];
String password = split[1];
authenticate(httpRequest, login, password);
}
}
chain.doFilter(request, response);
}
private void authenticate(HttpServletRequest httpRequest, String login, String password) {
ResourceOwnerPasswordResourceDetails details = new ResourceOwnerPasswordResourceDetails();
details.setAccessTokenUri(accessTokenUri);
details.setGrantType(GRANT_TYPE);
details.setClientId(clientId);
details.setClientAuthenticationScheme(AuthenticationScheme.query);
details.setUsername(login);
details.setPassword(password);
DefaultAccessTokenRequest tokenRequest = new DefaultAccessTokenRequest();
tokenRequest.setCurrentUri(httpRequest.getRequestURI());
try {
OAuth2AccessToken accessToken = provider.obtainAccessToken(details, tokenRequest);
OAuth2Authentication oauth2Authentication = tokenStore.readAuthentication(accessToken);
AccessTokenDetails accessTokenDetail = new AccessTokenDetails(accessToken.getValue());
oauth2Authentication.setDetails(accessTokenDetail);
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(oauth2Authentication);
} catch (OAuth2AccessDeniedException e) {
throw new AccessDeniedException("Wrong credentials !");
}
}
#Override
public void destroy() {
}
public static class AccessTokenDetails {
private static final String DEFAULT_TOKEN_TYPE = "bearer";
public final String tokenType;
public final String tokenValue;
public AccessTokenDetails(String tokenValue) {
this(DEFAULT_TOKEN_TYPE, tokenValue);
}
public AccessTokenDetails(String tokenType, String tokenValue) {
this.tokenType = tokenType;
this.tokenValue = tokenValue;
}
}
}
This filter check if there are basic authentication, and if yes, authenticate user. The authentication details are stored in internal class AccessTokenDetails. So, the token can be read in AuthorizationHeaderUtil :
public class AuthorizationHeaderUtil {
public static String getAuthorizationHeader() {
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
Object details = authentication.getDetails();
String tokenType = "";
String tokenValue = "";
if (details instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails oauth2Details = (OAuth2AuthenticationDetails) details;
tokenType = oauth2Details.getTokenType();
tokenValue = oauth2Details.getTokenValue();
} else if (details instanceof AccessTokenDetails) {
AccessTokenDetails accessTokenDetails = (AccessTokenDetails) details;
tokenType = accessTokenDetails.tokenType;
tokenValue = accessTokenDetails.tokenValue;
}
return String.format("%s %s", tokenType, tokenValue);
}
}
This class has been generated by JHipster, I add a check for the two authentication detail class I use.
I hope that will be useful.
Denis

Using Dropwizard Authentication manual

I'm currently using http://www.dropwizard.io/1.1.0/docs/manual/auth.html# Dropwizard-Authentication in my Application. But i like to do the authentication "manualy" which means from a specific login/logout API call on a not authenticated REST interface.
Is there any possibility to forward an REST Call to the Authentication?
#POST
#Path("login")
#Consumes(MediaType.APPLICATION_JSON)
#Timed
#UnitOfWork
public Optional<LoginResponse> login(LoginRequest request) {
// TODO forward login request to authentication
return null;
}
Thx in advance
Thx for helping me. I found a solution like that:
Adding an Authenticator to the REST Client
client = ClientBuilder.newClient();
authenticator = new Authenticator();
client.register(authenticator);
Setup the Authenticator on Login-Successfull
final UserAPIResponse response = create(request, UserAPI.PATH_ATTRIBUTE_DEFINITION_LOGIN);
if (response == null || response.isFailed()) {
connector.setupAuthenticator(null, null);
} else {
connector.setupAuthenticator(request.getUsername(), request.getPassword());
}
And here is the Authenticator
class Authenticator implements ClientRequestFilter {
#Override
public void filter(ClientRequestContext requestContext) throws IOException {
final MultivaluedMap<String, Object> headers = requestContext.getHeaders();
final String basicAuthentication = getBasicAuthentication();
if (basicAuthentication == null) return;
headers.add("Authorization", basicAuthentication);
}
void setup(String username, String password) {
this.user = username;
this.password = password;
}
private String getBasicAuthentication() {
if (user == null || password == null) return null;
final String token = this.user + ":" + this.password;
try {
return "BASIC " + DatatypeConverter.printBase64Binary(token.getBytes("UTF-8"));
} catch (final UnsupportedEncodingException ex) {
throw new IllegalStateException("Cannot encode with UTF-8", ex);
}
}
private String password;
private String user;
}
on the server side i have an Authenticator
public class UserAuthenticator implements Authenticator<BasicCredentials, User> {
UserAuthenticator(UserDAO userDAO) {
this.userDAO = userDAO;
}
#UnitOfWork
#Override
public Optional<User> authenticate(BasicCredentials credentials) throws AuthenticationException {
final String username = credentials.getUsername();
final Optional<DbUser> result = userDAO.getByName(username);
if (!result.isPresent()) return Optional.empty();
final DbUser user = result.get();
final String password = credentials.getPassword();
if (!StringUtils.equals(password, user.getPassword())) return Optional.empty();
if (!user.isOnline()) return Optional.empty();
user.handleAction();
userDAO.save(user);
return Optional.of(UserMgr.convert(user));
}
private final UserDAO userDAO;
}
And to get em working correctly:
SessionDao dao = new SessionDao(hibernateBundle.getSessionFactory());
ExampleAuthenticator exampleAuthenticator = new UnitOfWorkAwareProxyFactory(hibernateBundle)
.create(ExampleAuthenticator.class, SessionDao.class, dao);
So finally there is one REST-Call to login the User and the authentication is done on the result by the client automatically.

Spring Security Basic authentication for a single path next to token authentication

I have a custom ResourceServerTokenServices in place:
#Configuration
public class CloudSecurityConfig {
#Bean
protected MyResourceServerTokenServices() {
return new MyResourceServerTokenServices();
}
}
Then I have follogin ResourceServerConfigurerAdapter:
#Configuration
#EnableWebSecurity
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityResourceConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
http.authorizeRequests().accessDecisionManager(accessDecisionManager())
.antMatchers("/h2-console/**").anonymous()
.antMatchers("/health/**").permitAll()
.antMatchers("/v*/api-docs").permitAll().anyRequest()
.authenticated().and().httpBasic().and().headers()
.frameOptions().disable();
}
#Bean
protected UnanimousBased accessDecisionManager() {
List<AccessDecisionVoter<? extends Object>> voterList = new ArrayList<>();
WebExpressionVoter expressionVoter = new WebExpressionVoter();
expressionVoter.setExpressionHandler(new OAuth2WebSecurityExpressionHandler());
voterList.add(expressionVoter);
voterList.add(new AuthenticatedVoter());
return new UnanimousBased(voterList);
}
}
Now I need to add basic authentication (with inMemory credentials) for one single endpoint (lets say /myEndpoint**). How can I achieve this?
Thanks!

put and post method dropwizard-auth-jwt

I implemented Resful application using dropwizard framework. I use dropwizard-auth-jwt for my authentication with maven package:
<dependency>
<groupId>com.github.toastshaman</groupId>
<artifactId>dropwizard-auth-jwt</artifactId>
<version>1.0.2-0 </version>
</dependency>
for adding authentication for resources, I implemented sampleAuthenticator which is implemented Authenticator class that uses Principal class for it's authentication check.
public class UserAuthenticate implements Authenticator &ltJwtContext, MyUser> {
#Override
public Optional&ltMyUser> authenticate(JwtContext context) {
try {
final String subject = context.getJwtClaims().getSubject();
if ("authentication".equals(subject)) {
return Optional.of(new MyUser("admin", "pass"));
}
return Optional.empty();
}
catch (MalformedClaimException e) { return Optional.empty(); }
}
}
when MyUser implemented Principal:
public class MyUser implements Principal {
private String pass;
private String name;
public MyUser(String name, String pass) {
this.pass = pass;
this.name = name;
}
public MyUser( String name){
this.name = name;
}
public MyUser(){}
public String getPass() {
return pass;
}
#Override
public String getName() {
return name;
}
#Override
public String toString() {
return "MyUser{" +
"pass='" + pass + '\'' +
", name='" + name + '\'' +
'}';
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final MyUser myUser = (MyUser) o;
return Objects.equals(pass, myUser.pass) && Objects.equals(name, myUser.name);
}
#Override
public int hashCode() {
return Objects.hash(pass, name);
}
}
with this configurations, I needed add resource for crud operations. for get and delete there is no probelm. but when I add post or put when needs to add new object for body of request, I have error.
post:
#POST
#Path("/")
public Response create(#Auth MyUser admin, Body body) {
return Response
.status(Response.Status.OK)
.type(MediaType.APPLICATION_JSON)
.entity(true)
.build();
}
error:
Caused by: org.glassfish.jersey.server.model.ModelValidationException:
Validation of the application resource model has failed during
application initialization. [[FATAL] No injection source found for a
parameter of type public javax.ws.rs.core.Response
myGroup.resources.BodyResource.create(myGroup.api.MyUser,myGroup.api.Body)
at index 0.; source='ResourceMethod{httpMethod=POST,
consumedTypes=[application/json], producedTypes=[application/json],
suspended=false, suspendTimeout=0, suspendTimeoutUnit=MILLISECONDS,
invocable=Invocable{handler=ClassBasedMethodHandler{handlerClass=class
myGroup.resources.BodyResource,
handlerConstructors=[org.glassfish.jersey.server.model.HandlerConstructor#20411320]},
definitionMethod=public javax.ws.rs.core.Response
myGroup.resources.BodyResource.create(myGroup.api.MyUser,myGroup.api.Body),
parameters=[Parameter [type=class myGroup.api.MyUser, source=null,
defaultValue=null], Parameter [type=class myGroup.api.Body,
source=null, defaultValue=null]], responseType=class
javax.ws.rs.core.Response}, nameBindings=[]}', [WARNING] The
(sub)resource method create in myGroup.resources.BodyResource contains
empty path annotation.; source='public javax.ws.rs.core.Response
myGroup.resources.BodyResource.create(myGroup.api.MyUser,myGroup.api.Body)'
Jersey wants the #Path annotation to be at class level. See my answer here: Parse request parameters without writing wrapper class
I don't know which version of dropwizard you are using but I couldn't
make the combination of #POST and #Path("/something") annotation to
behave when a method is annotated. I'm getting HTTP ERROR 404.

How to use BCryptPasswordEncoder on account registration and on account authentication in spring?

I have created a web application based on Spring-Boot 1.2.2.RELEASE. I also have a database table where I manage user credentials. It has the basic columns e.g. id, username, and password. Also added salt column where I plan to store randomly generated salts per account.
I'm trying to use Spring Security and having some difficulty with authentication using the encoded password. So basically I have a Register Controller:
#Autowired
private UserRepository userRepository;
private BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
#RequestMapping(value = "/register", method = RequestMethod.POST)
public ResponseEntity<User> register(#RequestParam(value = "email_address") String emailAddress, #RequestParam(value = "password") String password) {
String username = emailAddress;
String salt = KeyGenerators.string().generateKey();
User user = new User();
user.setUsername(emailAddress);
user.setEmailAddress(emailAddress);
user.setSalt(salt);
user.setPassword(passwordEncoder.encode(username+salt+password));
user.setCreatedBy(1);
user.setCreatedOn(new Date());
user.setLastUpdatedby(1);
user.setLastUpdatedOn(new Date());
user.setStartDate(new Date());
return new ResponseEntity<User>(this.userRepository.save(user), HttpStatus.OK);
}
Then I implemented UserDetailsService like this:
#Service
protected static class ApplicationUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = this.userRepository.findByUsername(username);
if (user == null) {
return null;
}
List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER");
String password = passwordEncoder.encode(username+user.getSalt()+user.getPassword());
return new org.springframework.security.core.userdetails.User(username, password, auth);
}
}
However, since BCryptPasswordEncoder generates new result everytime even if the input is the same, how will it be able to authenticate?
EDIT:
Just wanted to add that if I don't use any encoder and store the password as plain text, I am able to authenticate just fine. But of course that's not what I want to do.
UPDATE 1
Here's config for WebSecurityConfigurerAdapter. Inside it I have the configure method.
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
protected static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private ApplicationUserDetailsService applicationUserDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(applicationUserDetailsService).passwordEncoder(passwordEncoder);
}
#Bean
#Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
}
UPDATE 2
I got it working by extending DaoAuthenticationProvider. See updated code below.
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
protected static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private ApplicationUserDetailsService applicationUserDetailsService;
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(applicationUserDetailsService);
authenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder());
return authenticationProvider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
#Bean
#Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
}
Also had to modify my RegisterController as shown below.
#RestController
public class RegisterController {
#Autowired
private UserRepository userRepository;
private BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
#RequestMapping(value = "/register", method = RequestMethod.POST)
public ResponseEntity<User> register(#RequestParam(value = "email_address") String emailAddress, #RequestParam(value = "password") String password) {
User user = new User();
user.setUsername(emailAddress);
user.setEmailAddress(emailAddress);
user.setPassword(passwordEncoder.encode(password));
user.setSalt(KeyGenerators.string().generateKey());
user.setCreatedBy(1);
user.setCreatedOn(new Date());
user.setLastUpdatedby(1);
user.setLastUpdatedOn(new Date());
user.setStartDate(new Date());
return new ResponseEntity<User>(this.userRepository.save(user), HttpStatus.OK);
}
}
UPDATE 3
Forgot to include the updated UserDetailService implementation.
#Service
protected static class ApplicationUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = this.userRepository.findByUsername(username);
if (user == null) {
return null;
}
List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER");
String password = passwordEncoder.encode(user.getPassword());
return new org.springframework.security.core.userdetails.User(username, password, auth);
}
}
I might need to remove the salt column now since it's basically useless.
Hopefully this helps others as well.