OAuth2 With Spring Boot Unauthorized (401) Response - java

I have implemented a spring boot application with oauth2. when I am trying to access token by providing clientId and Secret then unauthorized(401) response is returned.
oauth_client_detals table is designed in the oracle database with the following schema and secret column value is stored in BCrypt format.
insert into oauth_client_details(client_id,client_secret,web_server_redirect_uri,
scope,accsess_token_validity,refresh_token_validity,resource_id,authorized_grant_types,authorities,
additional_information,autoapprove) values ('web','{bcrypt}$2y$12$FCIQkEmh7ai/6oP99yNOEuWnKt9OjrGEczCxnEnFGDRSOHumOChQO',
'','READ,WRITE','900','3600','','password,authorization_code,refresh_token,implicit','ROLE_ADMIN,ROLE_USER,ROLE_MANAGER','','');
AuthorizationConfig.class
#Configuration
#EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private DataSource dataSource;
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("isAuthenticated()").tokenKeyAccess("permitAll()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource).passwordEncoder(new BCryptPasswordEncoder());
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
}
SecurityConfig.class
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private AuthEntryPoint authEntryPoint;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider1());
}
private AuthenticationProvider authenticationProvider1()
{
DaoAuthenticationProvider provider=new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(new BCryptPasswordEncoder());
return provider;
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().exceptionHandling().authenticationEntryPoint(authEntryPoint)
.and().authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
}
UserDetailsServiceImpl.class
#Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserDAO userDAO;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user= userDAO.findByUserName(username)
.orElseThrow(()->new UsernameNotFoundException("data not found with "+username));
return AuthUserDetails.builder(user);
}
}
AuthUserDetails.class
public class AuthUserDetails implements UserDetails{
private String userName;
private String password;
private List<GrantedAuthority> authorities;
private boolean accNonExpired;
private boolean accNonLocked;
private boolean credentialNonExpired;
private boolean active;
public AuthUserDetails()
{
}
public AuthUserDetails(boolean active, List<GrantedAuthority> authorities, String userName, String password,
boolean accNonExpired, boolean credentialNonExpired, boolean accNonLocked) {
this.active = active;
this.authorities = authorities;
this.userName = userName;
this.password = password;
this.accNonExpired = accNonExpired;
this.credentialNonExpired = credentialNonExpired;
this.accNonLocked = accNonLocked;
}
public static UserDetails builder(User user)
{
List<GrantedAuthority> grantedAuthorities=new ArrayList<>();
user.getRoles().forEach(role-> {
grantedAuthorities.add(new SimpleGrantedAuthority(role.getName().name()));
role.getPermissions().forEach(perm->{
grantedAuthorities.add(new SimpleGrantedAuthority(perm.getName().name()));
});
});
return new AuthUserDetails((user.getActive()==1),grantedAuthorities,user.getUserName(),user.getPassword(),
(user.getAccNonExpired()==1), (user.getCredentialNonExpired()==1),(user.getAccNonLocked()==1));
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
#Override
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return userName;
}
#Override
public boolean isAccountNonExpired() {
return accNonExpired;
}
#Override
public boolean isAccountNonLocked() {
return accNonLocked;
}
#Override
public boolean isCredentialsNonExpired() {
return credentialNonExpired;
}
#Override
public boolean isEnabled() {
return active;
}
}
User.class
#Entity
#Table(name="user56",schema = Schema.OAUTH2,uniqueConstraints = #UniqueConstraint(
columnNames = "username"
))
#Getter
#Setter
public class User {
#Id
#SequenceGenerator(name="user_id_gen",sequenceName = Schema.OAUTH2+".user_id_seq",initialValue = 1003,allocationSize = 1)
#GeneratedValue(generator = "user_id_gen",strategy = GenerationType.SEQUENCE)
#Column(name = "user_id")
private int userId;
#Column(name = "username")
private String userName;
#Column(name = "password")
private String password;
#Column(name = "email")
private String email;
#Column(name = "active")
private int active;
#Column(name = "acc_non_expired")
private int accNonExpired;
#Column(name = "credential_non_expired")
private int credentialNonExpired;
#Column(name = "acc_non_locked")
private int accNonLocked;
#ManyToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinTable(name = "role_user",
joinColumns = {#JoinColumn(name = "user_id",referencedColumnName = "user_id")},
inverseJoinColumns = {#JoinColumn(name = "role_id",referencedColumnName = "id")})
private Set<Role> roles;
}
OAuthClient.class
#Entity
#Table(name = "oauth_client_details",schema = Schema.OAUTH2)
#Getter
#Setter
public class OAuthClient {
#Id
#Column(name = "client_id")
private String clientId;
#Column(name="client_secret")
private String clientSecret;
#Column(name = "web_server_redirect_uri")
private String webServerRedirectUri;
#Column(name = "scope")
private String scope;
#Column(name = "accsess_token_validity")
private String accessTokenValidity;
#Column(name = "refresh_token_validity")
private String refreshTokenValidity;
#Column(name = "resource_id")
private String resourceId;
#Column(name="authorized_grant_types")
private String authorizedGrantType;
#Column(name = "authorities")
private String authorities;
#Column(name = "additional_information")
private String additionalInformation;
#Column(name = "autoapprove")
private String autoApprove;
}
response unauthorized(401) through postman
Updated
AuthEntryPoint.class
#Component
public class AuthEntryPoint implements AuthenticationEntryPoint {
Logger ERROR_LOGGER= LoggerFactory.getLogger(AuthEntryPoint.class);
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
ERROR_LOGGER.error("Unauthorized error : {}",authException.getMessage());
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,"Error : Unauthorized");
}
}
Updated 2
when I tried to run the application under the debugging mode then the following error will occur
FileNotFoundException#769

I have downloaded you project from here and, initially, I only have found some typo in the columns: accsess_token_validity and resource_id
After that I have added the required tables of your project and include some dummy information. For that reason, I'm pretty sure your problem is related with the password values in your oauth_client_details and user56 tables, because only when the stored value did not match with the expected one, I received 401.
Take into account you have defined two different BCryptPasswordEncoder instances:
provider.setPasswordEncoder(new BCryptPasswordEncoder()) for the users included in user56.
clients.jdbc(dataSource).passwordEncoder(new BCryptPasswordEncoder()) for the users included in oauth_clients_detail
Once I fixed it and store the expected ones, I achieved the endpoint returned the expected result:
As I told you in previous comments, there several classes will help you to find the cause of the error:
BasicAuthenticationFilter will get from the request the Basic Auth provided and executes the authentication process.
BasicAuthenticationConverter will extract Basic Auth provided from the request really.
JbdcClientDetailsService will get from oauth_clients_detail the information related with provided client_id in Basic Auth.
The following pictures are the most important to verify if your passwords match. That method will invoke twice: first for client_id / client_pass and second for username / password.
I have included some useful information in the "debug area"

Finally, I have found the place where the problem is occurring, When I send the request to the server then unauthorized(401) error occurs with the following message,
Encoded password does not look like BCrypt
So I have replaced the following code in SecurityConfig.class
#Bean
public PasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}
// replace to
#Bean
public PasswordEncoder getPasswordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
It works fine but still, I couldn't find why BCrytptPasswordEncoder is not worked even secret values are stored in BCrypt format. Anyway thank you very much #doctore for your answer

Related

Spring Security role based authentication - 403 Forbidden although user has ROLE_ADMIN

I want only users with role "Admin" to be able to get the "Welcome Admin!" message:
#GetMapping("/admin")
public String admin() {
return "Welcome Admin!";
}
So I added .antMatchers("/admin").access("hasRole('ADMIN')") to my SecurityConfig:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService userDetailsService;
#Autowired
private JwtFilter jwtFilter;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors();
http.csrf().disable().authorizeRequests()
.antMatchers("/authenticate", "/users/register","/products").permitAll()
.antMatchers("/admin").access("hasRole('ADMIN')") // HERE IT SHOULD CHECK FOR THE ADMIN ROLE
.anyRequest().authenticated().and().exceptionHandling()
.and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);;
}
}
The user that I use to access also has the role Admin:
But I always get 403 Forbidden as response:
Instead of adding the .antMatchers("/admin").access("hasRole('ADMIN')") to my SecurityConfig, I also tried to restrict the access with Annotations:
#PreAuthorize("hasRole('ROLE_ADMIN')")
//or #Secured({"ROLE_ADMIN"})
#GetMapping("/admin")
public String admin() {
return "Welcome Admin!";
}
But then I can access it with any user, even if they don't have the Admin Role (response status = 200). What am I doing wrong here?
Additional info:
My UserDetailsService:
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Autowired
private RoleRepository roleRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = null;
try {
user = userRepository.findByName(username);
} catch(Exception e){
String message = e.getMessage();
System.out.println(message);
}
if (user == null) {
return new org.springframework.security.core.userdetails.User(
" ", " ", true, true, true, true,
getAuthorities(Arrays.asList(
roleRepository.findByName("ROLE_USER"))));
}
return new org.springframework.security.core.userdetails.User(user.getName(), user.getPassword(), user.isEnabled(), true, true, true, getAuthorities(user.getRoles()));
}
private Collection<? extends GrantedAuthority> getAuthorities(
Collection<Role> roles) {
return getGrantedAuthorities(getPrivileges(roles));
}
private List<String> getPrivileges(Collection<Role> roles) {
List<String> privileges = new ArrayList<>();
List<Privilege> collection = new ArrayList<>();
for (Role role : roles) {
collection.addAll(role.getPrivileges());
}
for (Privilege item : collection) {
privileges.add(item.getName());
}
return privileges;
}
private List<GrantedAuthority> getGrantedAuthorities(List<String> privileges) {
List<GrantedAuthority> authorities = new ArrayList<>();
for (String privilege : privileges) {
authorities.add(new SimpleGrantedAuthority(privilege));
}
return authorities;
}
public UserDetails loadUserByEmail(String email) throws UsernameNotFoundException {
User user = userRepository.findByEmail(email);
return new org.springframework.security.core.userdetails.User(user.getName(), user.getPassword(), new ArrayList<>());
}
}
My jwtFilter.java:
#Component
public class JwtFilter extends OncePerRequestFilter {
#Autowired
private JwtUtil jwtUtil;
#Autowired
private CustomUserDetailsService service;
#Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
System.out.println("header:" + httpServletRequest.getHeader("Authorization"));
//get Authorization information from the request itself
String authorizationHeader = httpServletRequest.getHeader("Authorization");
String token = null;
String userName = null;
//check for its type, it must be Bearer + jwt
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
//get the token itself
token = authorizationHeader.substring(7);
//decrypt username
userName = jwtUtil.extractUsername(token);
}
if (userName != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = service.loadUserByUsername(userName);
if (jwtUtil.validateToken(token, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); // HERE IS THE getAuthorities function
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}
I'm creating the roles "Admin" and "User" in an extra class and also assign privileges to them as shown in this tutorial.
But actually I never need the privileges, I just couldn't get it done without them, so I left them in. Because without the privileges, I don't know how to get the GrantedAuthorities into my userDetails which are needed for the userDetails.getAuthorities() function in my jwtFilter class...:
#Component
public class SetupDataLoader implements
ApplicationListener<ContextRefreshedEvent> {
boolean alreadySetup = false;
#Autowired
private UserRepository userRepository;
#Autowired
private UserService userService;
#Autowired
private RoleRepository roleRepository;
#Autowired
private PrivilegeRepository privilegeRepository;
#Autowired
private PasswordEncoder passwordEncoder;
#Override
#Transactional
public void onApplicationEvent(ContextRefreshedEvent event) {
if (alreadySetup)
return;
Privilege readPrivilege
= createPrivilegeIfNotFound("READ_PRIVILEGE");
Privilege writePrivilege
= createPrivilegeIfNotFound("WRITE_PRIVILEGE");
List<Privilege> adminPrivileges = Arrays.asList(
readPrivilege, writePrivilege);
createRoleIfNotFound("ROLE_ADMIN", adminPrivileges);
createRoleIfNotFound("ROLE_USER", Arrays.asList(readPrivilege));
Role adminRole = roleRepository.findByName("ROLE_ADMIN");
Role userRole = roleRepository.findByName("ROLE_USER");
User harald = new User();
harald.setName("Harald");
harald.setPassword(passwordEncoder.encode("test"));
harald.setEmail("test#test.com");
harald.setRoles(Arrays.asList(adminRole));
harald.setEnabled(true);
userRepository.save(harald);
User hartmut = new User();
hartmut.setName("Hartmut");
hartmut.setPassword(passwordEncoder.encode("test"));
hartmut.setEmail("test#test.com");
hartmut.setRoles(Arrays.asList(adminRole));
hartmut.setEnabled(true);
userRepository.save(hartmut);
}
alreadySetup = true;
}
#Transactional
Privilege createPrivilegeIfNotFound(String name) {
Privilege privilege = privilegeRepository.findByName(name);
if (privilege == null) {
privilege = new Privilege(name);
privilegeRepository.save(privilege);
}
return privilege;
}
#Transactional
Role createRoleIfNotFound(
String name, Collection<Privilege> privileges) {
Role role = roleRepository.findByName(name);
if (role == null) {
role = new Role(name);
role.setPrivileges(privileges);
roleRepository.save(role);
}
return role;
}
}
My Role.java :
#Entity
#Table(name="roles")
#Data
#NoArgsConstructor
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "roles_privileges",
joinColumns = #JoinColumn(
name = "role_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(
name = "privilege_id", referencedColumnName = "id"))
private Collection<Privilege> privileges;
public Role(String name) {
this.name = name;
}
#Override
public String toString() {
return "Role{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
and my privilege.java:
#Entity
#Table(name="privileges")
#Data
#NoArgsConstructor
public class Privilege {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
public Privilege(String name) {
this.name = name;
}
#Override
public String toString() {
return "Privilege{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
I was able to fix the issue by prepending ROLE_
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_" + ADMIN));
1/ You need to enable the security annotation in your security config with adding this line :
#EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
prePostEnabled let you use #PreAuthorize,#PostAuthorize,etc and securedEnabled for using #Secured
2/ In order to say that only users with the role admin can access "/admin" you need to add an annotation like this :
`
#PreAuthorize("hasRole('ADMIN')")
// or #Secured({"ROLE_ADMIN"})
// or #PreAuthorize("hasAuthority('ROLE_ADMIN')")
#GetMapping("/admin")
public String admin() {
return "Welcome Admin!";
}
.antMatchers("/admin").access("hasRole('ADMIN')") should work too instead of using annotations.
3/ You need to be sure that your method loadByUsername returns the user admin when logging with his authorities list containing the authority "ROLE_ADMIN".
Annotate public class SecurityConfig extends WebSecurityConfigurerAdapter with #EnableGlobalMethodSecurity(prePostEnabled = true)
And,
#PreAuthorize("hasAuthority('ROLE_ADMIN')")
#GetMapping("/admin")
public String admin() {
return "Welcome Admin!";
}

toDoList Rest API: springBoot+H2+springSecutiry

I,m trying to do toDoList Rest API and have some problems with it.
Help me!
I decided to do multiuser project, separate users using basic auth of Spring Security. Here is my Entities: Task.java
#Entity
public class Task {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
private String title;
#JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate date;
private boolean status;
#ManyToOne
private User user;
public Task() {};
+Getters and Setters
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
private String username;
#JsonIgnore
private String password;
#JsonIgnore
private String[] roles;
public User() {};
+Getters and Setter;
Both entity interface extends CrudRepository. Task have #ManyToOne relationship to User.
Java helps me to create bd in h2 according to settings in application.properties
# H2
spring.h2.console.enabled=true
spring.h2.console.path=/h2
# Datasource
spring.datasource.url=jdbc:h2:./data/ToDoList
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver
spring.jpa.show-sql = true
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = update
Problem #1: I can't open h2 console. All crud operations are working well, i tested it it Postman, but i want to have access to console.
Then i implement UserDetailService to grab users attributes:
#Component
public class DetailsService implements UserDetailsService{
#Autowired
private UserRepository users;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = users.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(username + " was not found");
}
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
AuthorityUtils.createAuthorityList(user.getRoles())
);
}
}
And configure WebSecurity:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private DetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf().disable();
}
}
And here is Problem #2: how to take information about tasks of only one user?
In findAll method for example.
P.S. Sorry for my french (c).
UPD.
Try to grad User's data from SecurityContext. Create new file UserController. And that is works fine. Still have problems with access to H2 Console...
#RestController
#RequestMapping("/api/v1")
public class UserController {
#Autowired
private TaskRepository tasks;
#GetMapping("/tasks")
public List<Task> getAllTasks() {
List<Task> allTasks = tasks.findAll();
List<Task> userTasks = new ArrayList<>();
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String username = ((UserDetails)principal).getUsername();
for (Task t: allTasks) {
if (t.getUser().getUsername().equals(username)) {
userTasks.add(t);
}
}
return userTasks;
}
}

Questions about Spring Security

I am actually beginner in Spring framework. For today i've faced with problems and i want ask you a couple questions to figure it out.
I want to make simple user login/authorisation app with Android client.
First of all I want to post my code:
Model:
#Entity
#Table(name = "Users")
public class User {
public User() {
}
#Id
#GeneratedValue(generator = "increment")
#GenericGenerator(name = "increment", strategy = "increment")
#Column(name = "Id", unique = true, nullable = false)
private long id;
#Column(name = "userName", nullable = false)
private String userName;
#Column(name = "userPassword", nullable = false)
private String userPassword;
#Transient
private String confirmPassword;
public long getId() {
return id;
}
public String getUsername() {
return userName;
}
public String getPassword() {
return userPassword;
}
public String getConfirmPassword() {
return confirmPassword;
}
public void setId(long id) {
this.id = id;
}
public void setName(String userName) {
this.userName = userName;
}
public void setPassword(String userPassword) {
this.userPassword = userPassword;
}
public void setConfirmPassword(String confirmPassword) {
this.confirmPassword = confirmPassword;
}
}
Services:
User details:
#Service("userDetailsServiceImpl")
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
#Transactional(readOnly = true)
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
User user = userRepository.findByUserName(userName);
Set<GrantedAuthority> grantedAuthorities = new HashSet<GrantedAuthority>();
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantedAuthorities);
}
}
Impl of UserSerice interface:
#Service
public class UserServiceImpl implements UserService {
#Autowired
private UserRepository userRepository;
#Autowired
private BCryptPasswordEncoder passwordEncoder;
public void save(User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
userRepository.save(user);
}
public User findByUserName(String userName) {
return userRepository.findByUserName(userName);
}
}
User validator:
#Component
public class UserValidator implements Validator {
#Autowired
private UserService userService;
#Override
public boolean supports(Class aClass) {
return User.class.equals(aClass);
}
#Override
public void validate(Object o, Errors errors) {
User user = (User) o;
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "username", "Required");
if (user.getUsername().length() < 8 || user.getUsername().length() > 32) {
errors.rejectValue("username", "Size.userForm.username");
}
if (userService.findByUserName(user.getUsername()) != null) {
errors.rejectValue("username", "Duplicate.userForm.username");
}
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "Required");
if (user.getPassword().length() < 8 || user.getPassword().length() > 32) {
errors.rejectValue("password", "Size.userForm.password");
}
if (!user.getConfirmPassword().equals(user.getPassword())) {
errors.rejectValue("confirmPassword", "Different.userForm.password");
}
}
}
Impl of SecurityService
#Service
public class SecurityServiceImpl implements SecurityService {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private UserDetailsService userDetailsService;
#Override
public String findLoggedInUsername() {
Object userDetails = SecurityContextHolder.getContext().getAuthentication().getDetails();
if (userDetails instanceof UserDetails) {
return ((UserDetails) userDetails).getUsername();
}
return null;
}
#Override
public void autoLogin(String username, String password) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
authenticationManager.authenticate(authenticationToken);
if (authenticationToken.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
}
SecurityConfig:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
#ComponentScan("com.webserverconfig.user")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("userDetailsServiceImpl")
UserDetailsService userDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
PasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
And the lsat one -> Controller:
#RestController
#RequestMapping("/user")
public class UserController {
#Autowired
private UserService userService;
#Autowired
private SecurityService securityService;
#Autowired
private UserValidator userValidator;
#RequestMapping(value = "/registration", method = RequestMethod.POST)
#ResponseBody
#ResponseStatus(value = HttpStatus.CREATED)
public User registration(#RequestBody User user, BindingResult bindingResult, Model model) {
userValidator.validate(user, bindingResult);
if (bindingResult.hasErrors()) {
//What should i return here to my Android client ?
}
userService.save(user);
securityService.autoLogin(user.getUsername(), user.getConfirmPassword());
return user;
}
}
I am missing some classes here for saving space.
I am asking you for help me with my questions, please:
1) When i am trying to send JSON using Postman:
{
"id": 1,
"userName": "Andrew",
"userPassword": "apoyark123",
"confirmPassword": "apoyark123"
}
I am getting next erorr:
DefaultHandlerExceptionResolver - Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: Unrecognized field "userName" (class com.webserverconfig.user.entity.User), not marked as ignorable (4 known properties: "id", "name", "password", "confirmPassword"])
at [Source: java.io.PushbackInputStream#ddd416; line: 3, column: 16] (through reference chain: com.webserverconfig.user.entity.User["userName"]); nested exception is com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "userName" (class com.webserverconfig.user.entity.User), not marked as ignorable (4 known properties: "id", "name", "password", "confirmPassword"])
at [Source: java.io.PushbackInputStream#ddd416; line: 3, column: 16] (through reference chain: com.webserverconfig.user.entity.User["userName"])
Am i missing some annotation ?
This issue resolved by Mehdi in the first comment. !!!
Please help me with second question.
2) I am haven't tested security validation part yet, because this code doesn't working, but i have a question -> How my client will understand that login/authorisation have gone wrong/good ?
If we take a look on controller's method registration(...) -> what should i return to the client if first "if" false ?
What if validation is not correct ? What should i return to the client and how ?

Spring Boot OAuth2 and UserDetails

I am trying to implement an Auth server for my REST API in Spring Boot and I am struggling to autowire my own user repository into the the configuration. Could someone suggest how to do this correctly?
I have the following auth server configuration:
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("userDetailsService")
private UserDetailsService userDetailsService;
#Autowired
private AuthenticationManager authenticationManager;
#Value("${gigsterous.oauth.tokenTimeout:3600}")
private int expiration;
#Override
public void configure(AuthorizationServerEndpointsConfigurer configurer) throws Exception {
configurer.authenticationManager(authenticationManager);
configurer.userDetailsService(userDetailsService);
configurer.accessTokenConverter(accessTokenConverter());
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("gigsterous")
.secret("secret")
.accessTokenValiditySeconds(expiration)
.scopes("read", "write")
.authorizedGrantTypes("password", "refresh_token")
.resourceIds("resource");
}
/**
* Use custom jwt token converter to enhance the token with custom fields
*
* #return CustomTokenConverter
*/
#Bean
public CustomTokenConverter accessTokenConverter() {
return new CustomTokenConverter();
}
}
User service:
#Service("userDetailsService")
public class UserService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
public List<User> getUsers() {
return userRepository.findAll();
}
public User getUser(Long id) {
return userRepository.findById(id);
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return (UserDetails) userRepository.findOneByUsername(username);
}
}
User:
#Entity
#Table(name = "users")
#Getter
#Setter
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id", nullable = false, updatable = false)
private Long id;
#Column(name = "email", nullable = false, unique = true)
private String email;
#Column(name = "username", nullable = false, unique = true)
private String username;
#Column(name = "password", nullable = false)
private String password;
protected User() {
// hibernate
}
}
I am using Flyway and currently storing my users in H2 in memory database (this is working correctly so I omit this part of code from my question to avoid confusion).
When I try to authorise some user from my database using this command:
curl -X POST --user 'gigsterous:secret' -d 'grant_type=password&username=peter&password=password' http://localhost:9000/gigsterous-auth/oauth/token
I get the following response:
{"error":"unauthorized","error_description":"com.gigsterous.auth.domain.User cannot be cast to org.springframework.security.core.userdetails.UserDetails"}
Apparently, I cannot covert my user POJO into UserDetails object but I cannot figure out how to construct it correctly since it is an anonymous class.
It's been long time I've not worked on Spring, my memory might be not good. But if I'm not wrong, you want to use your User as UserDetails, you have to implement it, UserDetails is an interface. :D
And I recommend to change name of your class, User is already existed in OAuth2 library

Java + Spring MVC + Hibernate Persistence Error - Cant Persist User [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
Ok so I am building an ecommerce site using java spring mvc, spring security, maven, hibernate, and some other technologies however those should be the only ones relevant to my problem. I am doing all java config with no XML by the way (which I am currently considering revising since I am running into situations where xml just seems easier).
I would like to know why I am getting an no exceptions upon maven install but getting exceptions on server restart (tomcat 7).
I have tried to get this down to as simple as possible while still having a user and a role. When I am looking at the code, it really is pretty straight forward stuff - I am dying to find out why it errors out everytime.
Main Appconfig class
#EnableWebMvc
#Configuration
#ComponentScan({ "com.crutchsf.*" })
#EnableTransactionManagement
#Import({ SecurityConfig.class })
public class AppConfig {
#Bean
public SessionFactory sessionFactory() {
LocalSessionFactoryBuilder builder = new LocalSessionFactoryBuilder(dataSource());
builder
.scanPackages("com.crutchsf.users.model")
.addProperties(getHibernateProperties());
return builder.buildSessionFactory();
}
private Properties getHibernateProperties() {
Properties prop = new Properties();
prop.put("hibernate.format_sql", "true");
prop.put("hibernate.show_sql", "true");
prop.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
return prop;
}
#Bean(name = "dataSource")
public BasicDataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/test");
ds.setUsername("bobby");
ds.setPassword("password");
return ds;
}
#Bean
public HibernateTransactionManager txManager() {
return new HibernateTransactionManager(sessionFactory());
}
#Bean
public UserService userService() {
UserService userService = new MyUserDetailsService();
userService.setUserDao(new UserDaoImpl());
return userService;
}
#Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("validation");
return messageSource;
}
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/pages/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/img/**").addResourceLocations("/img/").setCachePeriod(31556926);
}
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
SecurityConfig.java
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("userDetailsService")
UserDetailsService userDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/admin/**")
.access("hasRole('ROLE_ADMIN')").and().formLogin()
.loginPage("/login").failureUrl("/login?error")
.usernameParameter("username")
.passwordParameter("password")
.and().logout().logoutSuccessUrl("/login?logout")
.and().csrf()
.and().exceptionHandling().accessDeniedPage("/403");
}
}
User class
#Entity
#Table(name = "users", catalog = "test")
public class User {
private Integer user_id;
private String username;
private String password;
private String passwordConf;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
#Id
#Column(name = "user_id")
#GeneratedValue
public Integer getUserId() {
return this.user_id;
}
#Column(name = "username", unique = true, nullable = false, length = 45)
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
#Column(name = "password", nullable = false, length = 60)
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
#Transient
public String getPasswordConf() {
return this.passwordConf;
}
public void setPasswordConf(String passwordConf) {
this.passwordConf = passwordConf;
}
}
UserRole class
#Entity
#Table(name = "user_roles", catalog = "test",
uniqueConstraints = #UniqueConstraint(
columnNames = { "role", "username" }))
public class UserRole{
private Integer userRoleId;
private User user;
private String role;
public UserRole() {
}
public UserRole(User user, String role) {
this.user = user;
this.role = role;
}
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "user_role_id",
unique = true, nullable = false)
public Integer getUserRoleId() {
return this.userRoleId;
}
public void setUserRoleId(Integer userRoleId) {
this.userRoleId = userRoleId;
}
public User getUser() {
return this.user;
}
public void setUser(User user) {
this.user = user;
}
#Column(name = "role", nullable = false, length = 45)
public String getRole() {
return this.role;
}
public void setRole(String role) {
this.role = role;
}
}
UserDao Interface
public interface UserDao {
User findByUserName(String username);
List<User> findAllUsers();
void addUser(User user);
}
UserDaoImpl
#Repository
public class UserDaoImpl implements UserDao {
#Autowired
private SessionFactory sessionFactory;
#SuppressWarnings("unchecked")
public User findByUserName(String username) {
List<User> users = new ArrayList<User>();
users = sessionFactory.getCurrentSession().createQuery("from User where username=?").setParameter(0, username)
.list();
if (users.size() > 0) {
return users.get(0);
} else {
return null;
}
}
#Override
#Transactional
public void addUser(User user) {
sessionFactory.getCurrentSession().persist(user);
}
UserService Interface
public interface UserService {
UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException;
void setUserDao(UserDao userDao);
void addUser(com.crutchsf.model.users.User user);
}
User Service Impl
#Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService, UserService {
#Autowired
private UserDao userDao;
#Transactional(readOnly=true)
#Override
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
com.crutchsf.model.users.User user = userDao.findByUserName(username);
List<GrantedAuthority> authorities = buildUserAuthority(user.getUserRole());
return buildUserForAuthentication(user, authorities);
}
private User buildUserForAuthentication(com.crutchsf.model.users.User user, List<GrantedAuthority> authorities) {
return new User(user.getUsername(), user.getPassword(), user.isEnabled(), true, true, true, 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.getRole()));
}
List<GrantedAuthority> Result = new ArrayList<GrantedAuthority>(setAuths);
return Result;
}
#Override
#Transactional
public void addUser(com.crutchsf.model.users.User user) {
this.userDao.addUser(user);
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
Database SQL used
CREATE TABLE users (
user_id int(11) NOT NULL AUTO_INCREMENT,
username VARCHAR(45) NOT NULL UNIQUE,
PRIMARY KEY (user_id));
Ok I shortened this quite a bit to the simple task of persisting a user. I cant get the server to startup without errors. There has to be something wrong in the code above.
In AppConfig.java if I change the line "scanPackages...." to the actual class name
com.crutchsf.users.model.User
then my server starts up fine but when it reaches the line in my userDao to be persisted
sessionFactory.getCurrentSession().persist(user);
I get a different error from hibernate "Cant find entity com.crutchsf.users.model.User"
Here is the main error I get. Basically I can choose which error I want to get now.
[public org.hibernate.SessionFactory com.crutchsf.config.AppConfig.sessionFactory()] threw exception; nested exception is java.lang.NullPointerException
thank you
On the surface, this doesn't appear to be anything to do with Spring MVC or Hibernate.
The error message is:
Factory method [public org.hibernate.SessionFactory com.crutchsf.config.AppConfig.sessionFactory()] threw exception; nested exception is java.lang.NullPointerException
If you have a stacktrace, you'll be able to see exactly what line the null reference occurred on. If not, you'll have to fire up a debugger (or insert println statements) to find out.
Either way, it appears that your AppConfig class has a bug in it, or you are not initialising it correctly. Look at the sessionConfig() method to see why it NPEs, and ensure that the appropriate references are not null before calling it.

Categories

Resources