I am getting white label error with 404 (not found) while I am loging in - spring-mvc

Here is my code for security configuration, I am getting white label error with 404 (not found) when I mention roles in hasAnyAuthority method, but when I mention only one role in hasAuthority it is working fine. Here I need to know do we need any authentication manager bean or authentication manager builder bean?
#EnableWebSecurity
#Configuration
public class WebSecurityConfig {
#Bean
public UserDetailsService userDetailsService() {
return new UserDetailsServiceImpl();
}
#Bean
AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf().disable()
.authorizeHttpRequests()
.antMatchers("/register").permitAll()
.and()
.authorizeHttpRequests()
.antMatchers("/signin").hasAnyAuthority("ROLE_ADMIN", "ROLE_USER")
.and()
.formLogin()
.permitAll()
.and()
.authenticationProvider(authenticationProvider());
return http.build();
}
}
Below is my controller class, I have not created any HTML pages. I am simply displaying message to check weather it is working or not, but it is not working. While I am mentioning the two roles which is user and admin in hasAnyAuthority and when I hit the /signin URL it is not working.
#RestController
public class UserRegistrationController {
#Autowired
private UserServices userService;
#Autowired
private PasswordEncoder passwordEncoder;
#PostMapping("/register")
public ResponseEntity<User> createUser(#Valid #RequestBody User user) throws Throwable {
String tempEmailID = user.getEmailId();
String encryptedPassword = passwordEncoder.encode(user.getPassword());
user.setPassword(encryptedPassword);
if (tempEmailID != null && !"".equals(tempEmailID)) {
User userObject = userService.fetchUserByEmailId(tempEmailID);
if (userObject != null) {
throw new ResourceNotFoundException("User with " + tempEmailID + " is already exist ");
}
userService.save(user);
}
return new ResponseEntity<User>(user, HttpStatus.CREATED);
}
#GetMapping("/signin")
public String loginPage() {
return "login sucess";
}
}

Related

Testing login api Spring Security with Postman

I am Working on a spring boot project for an e-commerce website, As a beginner, I try to add spring security in it so the problem is when I try to test my rest login API using postman I have a status code 200 and the body is always the default login page of spring security. I will be thankful for any advice or any solution.
Here is my user class :
public class User implements Serializable {
private static final long serialVersionUID = -2800960695811489984L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String firstName;
private String lastName;
#Column(nullable = false, unique = true)
private String username;
#Column(nullable = false)
private String address;
#Column(nullable = false, unique = true)
private String email;
private String password;
private boolean isEnabled;
#Column(name = "role" , nullable = false)
#Enumerated(EnumType.STRING)
private Role role;
Here is my Role enum :
public enum Role {
USER ,ADMIN
}
MyUserDetails Class :
public class MyUserDetails implements UserDetails {
String ROLE_PREFIX ="ROLE_";
private String email;
private String password;
private boolean active;
private Role role;
public MyUserDetails(User user) {
super();
this.email = user.getEmail();
this.password = user.getPassword();
this.active = user.isEnabled();
this.role = role;
}
public MyUserDetails(String email, String password, boolean enabled, Role role) {
super();
}
public static MyUserDetails create(User user) {
return new MyUserDetails(user.getEmail(), user.getPassword() ,user.isEnabled(), user.getRole());
}
Here is MyUserDetailsService :
#Service
#ToString
public class MyUserDetailsService implements UserDetailsService {
UserRepository userRepository;
#Autowired
public MyUserDetailsService(UserRepository userRepository) {
super();
this.userRepository = userRepository;
}
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
if (email == null || email.isEmpty()) {
throw new UsernameNotFoundException("email is Empty");
}
User user = userRepository.findByEmail(email);
if (user != null) {
return user.toCurrentUserDetails();
}
throw new UsernameNotFoundException( email + "is not found !!!");
}
}
Here is my RestController :
#CrossOrigin(origins = "*")
#RestController
#RequestMapping("/home")
public class HomeController {
private AuthenticationManager authenticationManager;
private MyUserDetailsService userDetailsService;
private UserRepository userRepository;
private Jwt jwtUtil;
#Autowired
public HomeController(AuthenticationManager authenticationManager, MyUserDetailsService userDetailsService
, UserRepository userRepository, Jwt jwtUtil) {
this.authenticationManager = authenticationManager;
this.userDetailsService = userDetailsService;
this.userRepository = userRepository;
this.jwtUtil = jwtUtil;
}
#PostMapping("/signin")
public ResponseEntity<ServerResp> addUser(#RequestBody User user) {
ServerResp response = new ServerResp();
try {
if (Validator.isUserEmpty(user)) {
response.setStatus(ResponseCode.BAD_REQUEST_CODE);
response.setMessage(ResponseCode.BAD_REQUEST_MESSAGE);
} else if (!Validator.isValidEmail(user.getEmail())) {
response.setStatus(ResponseCode.BAD_REQUEST_CODE);
response.setMessage(ResponseCode.INVALID_EMAIL_FAIL_MSG);
} else {
user.setRole(Role.USER);
user.setEnabled(true);
User reg = userRepository.save(user);
response.setStatus(ResponseCode.SUCCESS_CODE);
response.setMessage(ResponseCode.CUST_REG);
}
} catch (Exception e) {
response.setStatus(ResponseCode.FAILURE_CODE);
response.setMessage(e.getMessage());
}
return new ResponseEntity<ServerResp>(response, HttpStatus.ACCEPTED);
}
#PostMapping("/login")
public ResponseEntity<ServerResp> authentification(#RequestBody HashMap<String, String> credential) {
final String email = credential.get(WebConstants.USER_EMAIL);
final String password = credential.get(WebConstants.USER_PASSWORD);
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(email, password));
} catch (BadCredentialsException e) {
throw new UserNotFoundException(email);
}
final UserDetails userDetails = userDetailsService.loadUserByUsername(email);
final String jwt = jwtUtil.generateToken(userDetails);
ServerResp resp = new ServerResp();
resp.setStatus(ResponseCode.SUCCESS_CODE);
resp.setMessage(ResponseCode.SUCCESS_MESSAGE);
resp.setAUTH_TOKEN(jwt);
return new ResponseEntity<ServerResp>(resp, HttpStatus.OK);
}
}
Here is my Security Configuration Class :
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private MyUserDetailsService userDetailsService;
private JwtFilter jwtFilter;
#Autowired
DataSource datasource;
#Autowired
public SecurityConfiguration(MyUserDetailsService userDetailsService, JwtFilter jwtFilter) {
this.userDetailsService = userDetailsService;
this.jwtFilter = jwtFilter;
}
public SecurityConfiguration(boolean disableDefaults, MyUserDetailsService userDetailsService, JwtFilter jwtFilter) {
super(disableDefaults);
this.userDetailsService = userDetailsService;
this.jwtFilter = jwtFilter;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers("/resources/**", "/static/**", "/public/**").permitAll()
.antMatchers("/home/**").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**")
.hasRole("USER")
.anyRequest().authenticated().and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http
.formLogin()
//.loginPage("/home/login")
.usernameParameter("email")
.passwordParameter("password")
.permitAll()
.and()
.logout()
.permitAll();
http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
// TODO Auto-generated method stub
return super.authenticationManagerBean();
}
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
Here is my user in the database ( MySQL):
user in database
And finally, this is the result of the test in postman :
Test in Postman
Try sending your credentials using Authorization tab in postman, select authorization type to basic Auth.
I do the following steps with my Spring Boot app that has Spring Security. Note than my set up is most likely different from yours but the key here is the csfr token.
These are the steps I am following to do the login in Postman:
Start my Server
Open a chrome tab and go to developer tools and then the Network tab.
In the Network tab, I go to the DOC tab.
I then enter the url: http://localhost:9005/mywarehouse and press enter
This takes me to the Login Form where I then click on Create User
After the user is created I am taken back to the login form and login with the new user credentials.
In Postman, I submit a GET request such as http://localhost:9005/mywarehouse/products which returns the HTML of my login form instead of JSON.
Now back in chrome developer tab on the Doc tab I go to the last entry which is my login and I right click on it and select "Copy as cURL (bash)"
I then go back into Postman and click on the Import button and select "raw text" and then paste.
I then click on Continue and then Import. This opens up a new tab with a POST request and I click on send.
This returns HTML code. I locate the csfr token and copy it and then go to the Body tab and replace the csfr token that is currently there with the one returned from the HTML.
I then resend the POST request created by the Import and then I go back to my original GET request tab and resend and this time I get the JSON response I was expecting.
I have faced this same issue and I got a solution.
In postman go to the settings --> In General section --> turn off the automatically follow redirects.

How to return HTTP response from HandlerMethodArgumentResolver

I have some controller's method:
#RequestMapping("/")
#AuthorizedRNUser
public Object index(UserStateVO userStateVO) {
return userStateVO;
}
Also I have HandlerMethodArgumentResolver for UserStateVO parameter
public class UserStateArgumentHandlerResovler implements HandlerMethodArgumentResolver{
#Autowired
RNService service;
#Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.getMethod().isAnnotationPresent(AuthorizedRNUser.class) && methodParameter.getParameterType() == UserStateVO.class;
}
#Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
UserStateVO userState = service.getUserState();
if (isNull(userState))
// here i need to return 403 HTTP response
throw new RuntimeException("User is not allowed");
return userState;
}
}
And if the UserStateVO is null I need to return 403 HTTP response, but I do not know is it possible? How best to check UserStateVO and pass it into a controller or return HTTP response?
Use the same method as handling exceptions in MVC exception-handling-in-spring-mvc
Add your custom exception, e.g
public class BadRequestException extends RuntimeException {
private static final long serialVersionUID = 1L;
public BadRequestException(String message) {
super(message);
}
}
And either annotate it with #ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "User is not allowed") or add #ControllerAdvice class with method like
#ExceptionHandler(value = { BadRequestException.class })
#ResponseStatus(value = HttpStatus.FORBIDDEN)
#ResponseBody
public Map<String, String> handleBadRequestException(BadRequestException e) {
Map<String, String> retMessages = new HashMap<>();
retMessages.put("message", e.getMessage());
return retMessages;
}
what remains is just to throw it
if (isNull(userState))
// here i need to return 403 HTTP response
throw new BadRequestException("User is not allowed");

Authentication Failed: No AuthenticationProvider

I have two levels of access in the application: for everyone and only for authorized.
I'm login as a registered user,
but if I try to request data that is protected I get an error:
Authentication Failed: No AuthenticationProvider found for
com.company.security.tokenAuth.TokenAuthentication
My TokenAuthentication class:
public class TokenAuthentication extends AbstractAuthenticationToken {
private static final long serialVersionUID = -4021530026682433724L;
private UserDetails principal;
private String token;
public TokenAuthentication(String token) {
super(new HashSet<>());
this.token = token;
}
public TokenAuthentication(String token, Collection<? extends GrantedAuthority> authorities,
boolean isAuthenticated, UserDetails principal) {
super(authorities);
this.principal = principal;
this.setAuthenticated(isAuthenticated);
}
#Override
public Object getCredentials() {
return null;
}
#Override
public UserDetails getPrincipal() {
return principal;
}
public String getToken() {
return token;
}
}
My TokenAuthenticationProvider class:
#Component
public class TokenAuthenticationProvider implements AuthenticationProvider {
private TokenService tokenService;
private AccountDetailsService accountService;
public TokenAuthenticationProvider(TokenService tokenService, AccountDetailsService accountService) {
this.tokenService = tokenService;
this.accountService = accountService;
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (authentication instanceof TokenAuthentication) {
return processAuthentication((TokenAuthentication) authentication);
} else {
authentication.setAuthenticated(false);
return authentication;
}
}
#Override
public boolean supports(Class<?> aClass) {
return aClass.equals(TokenAuthentication.class);
}
private TokenAuthentication processAuthentication(TokenAuthentication authentication) {
try {
Account token = tokenService.parseToken(authentication.getToken());
Set<GrantedAuthority> authorities = new HashSet<>();
authorities.add(new SimpleGrantedAuthority(token.getRole().name()));
return new TokenAuthentication(authentication.getToken(), authorities,
true, new AccountDetails((Account) accountService.loadUserByUsername(token.getEmail())));
} catch (ValidationException e) {
throw new AuthenticationServiceException("Invalid token");
} catch (Exception e) {
throw new AuthenticationServiceException("Token corrupted");
}
}
}
What is my problem?
Thank you for your help.
I found the answer.
I changed my authentication according to the project by reference https://github.com/oharsta/spring-jwt/tree/50f130ee5d63d746cc9d7adf2f0d8f085327a84a
And fixed role, since I have only one user and one role in the form of the enum. And during authentication, the list of roles is used.
After solving this problem, everything worked.

How to get custom UserDetailService Object in Resource Server in spring-security-oauth2?

I have separate authorization server and resource server.
Authorization server is pointing to a separate database. I haves used CustomUserDetailService for user related information.
I have used CustomTokenEnhancer to have additional information apart from the token in the response.
#Configuration
public class OAuth2Configuration {
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter implements EnvironmentAware {
private static final String ENV_OAUTH = "authentication.oauth.";
private static final String PROP_CLIENTID = "clientid";
private static final String PROP_SECRET = "secret";
private static final String PROP_TOKEN_VALIDITY_SECONDS = "tokenValidityInSeconds";
private RelaxedPropertyResolver propertyResolver;
#Autowired
private DataSource dataSource;
#Autowired
private CustomUserDetailService userDetailsService;
#Bean
public TokenStore tokenStore() {
return new CustomTokenStore(dataSource);
}
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.tokenStore(tokenStore())
.userDetailsService(userDetailsService)
.tokenEnhancer(tokenEnhancer())
.accessTokenConverter(accessTokenConverter())
.authenticationManager(authenticationManager);
}
#Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
#Bean
public DefaultAccessTokenConverter accessTokenConverter() {
return new DefaultAccessTokenConverter();
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer)
throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
}
}
CustomUserDetailService Class:
#Service
public class CustomUserDetailService implements UserDetailsService {
#Autowired
private AccountRepository accountRepository;
#Override
public UserDetails loadUserByUsername(String username) {
Account account = accountRepository.getByEmail(username);
if(account == null) {
throw new UsernameNotFoundException(username);
}
return new MyUserPrincipal(account);
}
}
CustomTokenEnhancer Class:
public class CustomTokenEnhancer implements TokenEnhancer {
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
MyUserPrincipal user = (MyUserPrincipal) authentication.getPrincipal();
final Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("user_information", user.getAccount());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}
Request/Response
http://localhost:9191/authserver/oauth/token
{
"access_token": "fddb571e-224e-4cd7-974e-65104dd24b41",
"token_type": "bearer",
"refresh_token": "eb412b00-9e4e-4d6c-86d8-324d999b5f08",
"expires_in": 100,
"scope": "read write",
"account_information": {
"id": 14,
"firstname": "name",
"lastname": "lastname",
}
}
At resource server side, I have used RemoteTokenSerice to verify the the token presented by user is valid or not.
#Configuration
#EnableResourceServer
public class OAuthResourceConfig extends ResourceServerConfigurerAdapter {
private TokenExtractor tokenExtractor = new BearerTokenExtractor();
#Override
public void configure(HttpSecurity http) throws Exception {
http.addFilterAfter(new OncePerRequestFilter() {
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (tokenExtractor.extract(request) == null) {
SecurityContextHolder.clearContext();
}
filterChain.doFilter(request, response);
}
}, AbstractPreAuthenticatedProcessingFilter.class);
http.csrf().disable();
http.authorizeRequests().anyRequest().authenticated();
}
#Bean
public AccessTokenConverter accessTokenConverter() {
return new DefaultAccessTokenConverter();
}
#Bean
#Primary
public RemoteTokenServices remoteTokenServices(final #Value("${auth.server.url}") String checkTokenUrl,
final #Value("${auth.server.clientId}") String clientId,
final #Value("${auth.server.clientsecret}") String clientSecret) {
final RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
remoteTokenServices.setCheckTokenEndpointUrl(checkTokenUrl+"?name=value");
remoteTokenServices.setClientId(clientId);
remoteTokenServices.setClientSecret(clientSecret);
remoteTokenServices.setAccessTokenConverter(accessTokenConverter());
return remoteTokenServices;
}
}
So It is working properly and when I make a request to resource server with token, it processes the request if the token is valid. My question is I want to get Account object in resource server. I tried with below:
Account account = (Account)SecurityContextHolder.getContext().getAuthentication().getPrincipal()
But it gives string and not the complete user define object and hence it throws the exception.How to get Account object in any controller in Resource server?
{
"timestamp": 1499334657703,
"status": 500,
"error": "Internal Server Error",
"exception": "java.lang.ClassCastException",
"message": "java.lang.String cannot be cast to Account",
"path": "/secure"
}
I tried with link but is it possible to inject two token services, RemoteTokenService and CustomUserInfoTokenServices both?
Also I think here spring makes internal call from Resource Server to Authorization Server (http://localhost:9191/authserver/oauth/check_token?token=d8dae984-7bd8-4aab-9990-a2c916dfe667) to validate the token.
Is there any way I can get those information in controller without calling this endpoint again.
Response:
{
"exp": 1499333294,
"account_information": {
"accountid": 14,
"firstname": "fname",
"lastname": "lname",
},
"user_name": "abc#abc.com",
"client_id": "clientId",
"scope": [
"read",
"write"
]
}
I have overridden below method and added some logic.
public class CustomAccessTokenConverter extends DefaultAccessTokenConverter{
private UserAuthenticationConverter userTokenConverter = new DefaultUserAuthenticationConverter();
#Override
public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
Map<String, String> parameters = new HashMap<String, String>();
#SuppressWarnings("unchecked")
Set<String> scope = new LinkedHashSet<String>(map.containsKey(SCOPE) ? (Collection<String>) map.get(SCOPE)
: Collections.<String>emptySet());
Authentication user = userTokenConverter.extractAuthentication(map);
String clientId = (String) map.get(CLIENT_ID);
parameters.put(CLIENT_ID, clientId);
parameters.put("account_information", String.valueOf((((Map) map.get("account_information")).get("accountid"))));
#SuppressWarnings("unchecked")
Set<String> resourceIds = new LinkedHashSet<String>(map.containsKey(AUD) ? (Collection<String>) map.get(AUD)
: Collections.<String>emptySet());
Map<String, Serializable> extensions = new HashMap<String, Serializable>();
extensions.put("account_information", (HashMap) map.get("account_information"));
OAuth2Request request = new OAuth2Request(parameters, clientId, null, true, scope, resourceIds, null, null,
extensions);
return new OAuth2Authentication(request, user);
}
}
Resource Server Class
#Bean
public AccessTokenConverter accessTokenConverter() {
//return new DefaultAccessTokenConverter();
return new CustomAccessTokenConverter();
}
#Bean
#Primary
public RemoteTokenServices remoteTokenServices(final #Value("${auth.server.url}") String checkTokenUrl,
final #Value("${auth.server.clientId}") String clientId,
final #Value("${auth.server.clientsecret}") String clientSecret) {
final RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
remoteTokenServices.setCheckTokenEndpointUrl(checkTokenUrl+"?name=value");
remoteTokenServices.setClientId(clientId);
remoteTokenServices.setClientSecret(clientSecret);
remoteTokenServices.setAccessTokenConverter(accessTokenConverter());
return remoteTokenServices;
}
Now I can get additional information in controller.
OAuth2Authentication authentication = (OAuth2Authentication)SecurityContextHolder.getContext().getAuthentication();
Map<String, Serializable> map = authentication.getOAuth2Request().getExtensions();
I use in this way.
after call /oauth/token, I can get below, and member_id is additional field I added.
{
"access_token": "this is access token",
"token_type": "bearer",
"refresh_token": this is refreshtoken",
"expires_in": 3599,
"scope": "web",
"member_id": "d2lsbGlhbQ",
"jti": "79b9b523-921d-45c1-ba97-d3565f1d68b7"
}
after decode the access token, I can see this custom field member_id in it.
below are what I do in my Resource Server.
Declear Bean DefaultTokenService in configuration class
#Bean
#Primary
public DefaultTokenServices tokenServices() throws IOException {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
return defaultTokenServices;
}
here I inject resource into my controller.
#Autowired
private ResourceServerTokenServices resourceServerTokenServices;
#GetMapping("/addition")
public Map<String, Object> addition() {
Map<String, Object> response = new HashMap<>();
response.put("member_id", resourceServerTokenServices.readAccessToken(((OAuth2AuthenticationDetails) SecurityContextHolder.getContext().getAuthentication().getDetails()).getTokenValue()).getAdditionalInformation().get("member_id"));
return response;
}
then I call this /addition, I can see the response.
{
"member_id": "d2lsbGlhbQ"
}
I'm a newer to oAuth2 with JWT, so I have do some research on internet, but cannot find a sensitive method to get it from resource server. so I try some method to get this. hope it works.

User can't be cast to to com.example.security.CustomUserDetails Spring Security

I'm trying to set my user on to a course object that I created and I keeping getting the error org.springframework.security.core.userdetails.User cannot be cast to com.example.security.CustomUserDetails I know I'm getting the user details from the session, because when I run it in debug mode I can see the name of the currently logged in user, so I think I'm somewhat close to a solution.
Here's my Controller
#RequestMapping(value="createCourse", method=RequestMethod.POST)
public String createCoursePost (#ModelAttribute Course course, Long userId, ModelMap model, Authentication auth)
{
CustomUserDetails myUserDetails = (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
userId = myUserDetails.getUser().getId();
User user = userRepo.findOne(userId);
course.setUser(user);
courseRepo.save(course);
return "redirect:/courses";
}
Here's my UserDetailsServiceImpl
#Service
#Qualifier("customUserDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserRepository userRepo;
#Transactional
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
com.example.domain.User user = userRepo.findByUsername(username);
List<GrantedAuthority> authorities = buildUserAuthority(user.getRoles());
return buildUserForAuthentication(user, authorities);
}
private User buildUserForAuthentication(com.example.domain.User user,
List<GrantedAuthority> authorities) {
return new User(user.getUsername(), user.getPassword(), authorities);
}
private List<GrantedAuthority> buildUserAuthority(Set<UserRole> userRoles) {
Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
// Build user's authorities
for (UserRole userRole : userRoles) {
setAuths.add(new SimpleGrantedAuthority(userRole.getRoleName()));
}
return new ArrayList<GrantedAuthority>(setAuths);
}
Here's my customUserDetails, it's possible that me getting and setting the user could be redundant, but I saw an example with this, and so I'm not really sure what to do with this.
public class CustomUserDetails extends User implements UserDetails{
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
private static final long serialVersionUID = 2020921373107176828L;
public CustomUserDetails () {}
public CustomUserDetails (User user) {
super(user);
}
#Override
public Set<Authorities> getAuthorities() {
return super.getAuthorities();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
Here's my webSecurityConfig
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private static PasswordEncoder encoder;
#Autowired
private UserDetailsService customUserDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.csrfTokenRepository(csrfTokenRepository());
http
.authorizeRequests()
.antMatchers("/", "/home", "/register", "/courses", "/editCourse", "/sets", "/search", "/viewCourse/{courseId}", "/fonts/glyphicons-halflings-regular.ttf","fonts/glyphicons-halflings-regular.woff", "fonts/glyphicons-halflings-regular.woff", "/viewCourse/post/{postId}").permitAll()
.anyRequest().authenticated();
http
.formLogin()
.loginPage("/login")
.usernameParameter("username").passwordParameter("password")
.permitAll()
.and()
.logout()
.permitAll()
.logoutSuccessUrl("/loggedout")
.and()
.sessionManagement()
.maximumSessions(1);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService)
.passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
if(encoder == null) {
encoder = new BCryptPasswordEncoder();
}
return encoder;
}
private CsrfTokenRepository csrfTokenRepository()
{
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setSessionAttributeName("_csrf");
return repository;
}
}
I solved this just in case anyone else sees this. I think my problem was that I wasn't returning userdetails in the userdetailsimpl class. And I also had to update my controller. So here's my new code
Controller
#RequestMapping(value="createCourse", method=RequestMethod.POST)
public String createCoursePost (#ModelAttribute Course course, Long userId, ModelMap model)
{
CustomUserDetails myUserDetails = (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
userId = myUserDetails.getUser().getId();
User user = userRepo.findOne(userId);
course.setUser(user);
courseRepo.save(course);
return "redirect:/courses";
}
And userDetailsServiceImpl
#Service
#Qualifier("customUserDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserRepository userRepo;
#Transactional
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
com.example.domain.User user = userRepo.findByUsername(username);
CustomUserDetails customUserDetails = new CustomUserDetails(user);
customUserDetails.setUser(user);
return customUserDetails;
}
}

Resources