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 ?
Related
I have already done user register and login. But I want to encrypt the password when create a profile.
This is my current configuration
MongoDB Connection
spring.data.mongodb.uri= mongodb://127.0.0.1:27017/Student
server.port=8080
Model Class:
#Document
#AllArgsConstructor
#NoArgsConstructor
#Data
public class User {
#Id
#Indexed
private String id;
#Indexed
private String address;
#Indexed
private String name;
#Indexed
private String email;
#Indexed
private String password;
#Indexed
private String role;
}
Repository Class:
public interface userReporsitory extends MongoRepository<User,String> {
Optional<User> findByEmail(String email);
List<User> findAllByRole(String role);
}
Service Class:
#AllArgsConstructor
#Service
public class userService {
private userReporsitory userReporsitory;
public User saveUser(User user){
return userReporsitory.save(user);
}
public User login(User user){
User response = userReporsitory.findByEmail(user.getEmail()).orElseThrow(()->new RuntimeException("User Not Found"));
if(!response.getPassword().equals(user.getPassword())){
throw new RuntimeException("Bad Credincials");
}
return response;
}
public List<User> findAllUsers(){
return userReporsitory.findAllByRole("user");
}
}
Controller Class:
#CrossOrigin
#RestController
#AllArgsConstructor
#RequestMapping("api/v1/user")
public class userController {
private userService userService;
#PostMapping("/create")
public ResponseEntity<User> save(#RequestBody User user){
HttpStatus status = HttpStatus.EXPECTATION_FAILED;
User response = userService.saveUser(user);
if(response != null){
status = HttpStatus.CREATED;
}
return new ResponseEntity<>(response, status);
}
#PostMapping("/login")
public ResponseEntity<User> login(#RequestBody User user){
return new ResponseEntity<>(userService.login(user),HttpStatus.ACCEPTED);
}
#GetMapping("/userList")
public ResponseEntity<List<User>> userList(){
return new ResponseEntity<>(userService.findAllUsers(),HttpStatus.ACCEPTED);
}
}
Use
BCryptPasswordEncoder
Class while saving the Password in DataBase.it will convert the normal text to RandomValue.
Define the BCryptPasswordEncoder In config Class.
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
Repository Class :
#Autowired
private PasswordEncoder passwordEncoder;
public User newUserAccount(UserDto accountDto) {
User user = new User();
user.setFirstName(accountDto.getFirstName());
user.setLastName(accountDto.getLastName());
user.setPassword(passwordEncoder.encode(accountDto.getPassword()));
return repository.save(user);
}
So I am working on a Spring boot project and security is one of the things I want to have in this project.
I am having this problem where my code always goes to the BadCredentialsException, but I thought my credentials are correct.
My AuthenticationController:
#RestController
#CrossOrigin
public class JwtAuthenticationController {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Autowired
private JwtUserDetailsService userDetailsService;
#RequestMapping(value = "/authenticate", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(#RequestBody JwtRequest authenticationRequest) throws Exception {
authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
final UserDetails userDetails = userDetailsService
.loadUserByUsername(authenticationRequest.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new JwtResponse(token));
}
private void authenticate(String username, String password) throws Exception {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (DisabledException e) {
throw new Exception("USER_DISABLED", e);
} catch (BadCredentialsException e) {
throw new Exception("INVALID_CREDENTIALS", e);
}
}
#RequestMapping(value = "/register", method = RequestMethod.POST)
public ResponseEntity<?> saveUser(#RequestBody UserDTO user) throws Exception {
return ResponseEntity.ok(userDetailsService.save(user));
}
}
My UserDetailsService:
#Service
public class JwtUserDetailsService implements UserDetailsService {
#Autowired
private UserDao userDao;
#Autowired
private PasswordEncoder bcryptEncoder;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
DAOUser user = userDao.findByUsername(username);
if (user == null){
throw new UsernameNotFoundException("User not found with username: " + username);
}
return new User(user.getUsername(), user.getPassword(), new ArrayList<>());
}
public DAOUser save(UserDTO user) {
DAOUser newUser = new DAOUser();
newUser.setUsername(user.getUsername());
newUser.setPassword(bcryptEncoder.bCryptPasswordEncoder().encode(user.getPassword()));
return userDao.save(newUser);
}
}
My WebSecurityConfig:
#Configuration
#AllArgsConstructor
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {//provides security for endpoints
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private UserDetailsService jwtUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
private final AccountService accountService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()//So we can send post requests without being rejected(if we using form based indication we want to enable this)
.authorizeRequests()
.antMatchers("/authenticate","/register")
.permitAll()//any request that goes trough that end point we want to allow.
.anyRequest()
.authenticated().and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
#Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider =
new DaoAuthenticationProvider();
provider.setPasswordEncoder(bCryptPasswordEncoder);
provider.setUserDetailsService(accountService);
return provider;
}
}
My RequestFilter:
#Component
public class JwtRequestFilter extends OncePerRequestFilter {
#Autowired
private JwtUserDetailsService jwtUserDetailsService;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String authorizationHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
if (authorizationHeader != null && authorizationHeader.startsWith("")){
jwt = authorizationHeader.substring(7);
username = jwtTokenUtil.extractUsername(jwt);
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null){
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwt, userDetails)){
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
}
My PasswordEncoder:
#Configuration
public class PasswordEncoder{
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
}
My UserDao
#Entity
#Table(name = "myusers")
public class DAOUser {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column
private String username;
#Column
#JsonIgnore
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
My UserDao Interface
I know the name is confusing, but I just followed the tutorial and he gave it this name, I have not changed it, because I want it to work before I rename files.
#Repository
public interface UserDao extends CrudRepository<DAOUser, Integer> {
DAOUser findByUsername(String username);
}
I think these are all the files you need to help me. if you need more, just ask and I will upload them.
Can anyone help me with this problem?
Thanks!!
if you read the spring documentation on passwords (which you should of done before you ask here)
you will see that the format when storing a password is:
{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
While when you are storing it you are just storing it and not adding the {bcrypt} prefix to the generated string before you store it.
newUser.setPassword(bcryptEncoder.bCryptPasswordEncoder().encode(user.getPassword()));
So when you are later providing a password, you are getting bad password since spring does not know what encoder to use when decoding the password.
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!";
}
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
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.