I have been trying to add a create method to my spring boot security app but, when I use post mapping I get that error. Also, my id is auto-incremented in db. I am not sure but maybe the error is because of it. I don't know how to write an auto-incremented value in the request body.
{"timestamp":"2020-08-
23T00:43:31.062+00:00","status":403,"error":"Forbidden","message":"","path":"/createUser"}
The body that i am trying to post:
{
"id": 3,
"userName": "Adminn",
"password": "pss",
"active": true,
"role": "ROLE_ADMIN"
}
Request Body for Post Mapping
[1]: https://i.stack.imgur.com/uqoD0.png
My home resource class
package io.javabrains.springsecurity.jpa;
#RestController
public class HomeResource {
#Autowired
private UserRepository userRepo;
#GetMapping("/")
public String home() {
return ("<h1>Welcome</h1>");
}
#GetMapping("/user")
public String user() {
return ("Welcome User");
}
#GetMapping("/admin")
public String admin() {
return ("<h1>Welcome Admin</h1>");
}
#GetMapping("/users/{id}")
public Optional<User> retriveUser(#PathVariable int id)
{
return userRepo.findById(id);
}
#PostMapping("/createUser")
public void createUser(#RequestBody User myuser) {
User savedUser=userRepo.save(myuser);
}
/*#GetMapping("/createUser") // it is working
public String addUser() {
User newuser= new User();
newuser.setUserName("new");
newuser.setPassword(new BCryptPasswordEncoder().encode("pass"));
newuser.setRole("ROLE_ADMIN");
newuser.setActive(true);
userRepo.save(newuser);
return "user booked";
}*/
}
My Spring App Class
#SpringBootApplication
#EnableJpaRepositories(basePackageClasses = UserRepository.class)
public class SpringsecurityApplication implements CommandLineRunner{
#Autowired
UserRepository userRepository;
public static void main(String[] args) {
SpringApplication.run(SpringsecurityApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
// TODO Auto-generated method stub
System.out.println("Application Running.");
User adminUser= new User();
adminUser.setUserName("Admin");
adminUser.setPassword(new BCryptPasswordEncoder().encode("pass"));
adminUser.setRole("ROLE_ADMIN");
adminUser.setActive(true);
userRepository.save(adminUser);
User newUser= new User();
newUser.setUserName("User");
newUser.setPassword(new BCryptPasswordEncoder().encode("pass"));
newUser.setRole("ROLE_USER");
newUser.setActive(true);
userRepository.save(newUser);
}
}
User Class
package io.javabrains.springsecurity.jpa.models;
#Entity
#Table(name="app_user")
public class User {
#Id
#GeneratedValue(strategy =GenerationType.AUTO)
private int id;
private String userName;
private String password;
private boolean active;
private String role;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
Security Config Class
package io.javabrains.springsecurity.jpa;
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder ()
{
return new BCryptPasswordEncoder();
}
#Autowired
UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin").hasAnyRole("ADMIN")
.antMatchers("/user").hasAnyRole("ADMIN","USER")
.antMatchers("/","/createUser").permitAll()
.and().formLogin();
}
}
Because CSRF protection for state-changing HTTP verbs, such as POST, is enabled by default. You can either disable it or include CSRF token in your web page, and subsequently in your HTTP request.
What if you modify your code this way:
.antMatchers(HttpMethod.POST, "/createUser").permitAll()
First of all is a bad practice try to map the JSON request to an entity class. First you should use a DTO class.
Do that first and see what happend
Related
I want to create a login method for my spring application. But when I try to call the
getUserByAuthentication
method, I get a null pointer exception. Here is my code:
Function calling the error:
#PostMapping(path = "/online")
public ResponseEntity<?> onlineRequest(#RequestBody OnlineRequest onlineRequest) {
User user = null;
UserManager userManager = new UserManager();
user = userManager.getUserByAuthentication(onlineRequest.getUsername(), onlineRequest.getPassword());
if (user!=null){
user.setLatestTimeStamp(System.currentTimeMillis());
return new ResponseEntity<>("You are now online, Enjoy!", HttpStatus.OK);
} else {
return new ResponseEntity<>("Invalid login", HttpStatus.valueOf(403));
}
}
Get User by Authentication class:
public class UserManager {
#Autowired
private UserRepository userRepository;
public User getUserByID(int id){
return userRepository.findById(id).get();
}
public User getUserByAuthentication(String name, String password){
Iterable<User> userList = userRepository.findAll();
ArrayList<User> users = new ArrayList<>();
userList.forEach(users::add);
User user = null;
for (User u : users){
if (u.getUsername().equals(name) && u.getPassword().equals(password)){
user = u;
}
}
return user;
}
}
Repository:
#Repository
public interface UserRepository extends CrudRepository<User, Integer> {
}
User class:
#Entity
#Table
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
//Initialize
private String username;
private String password;
private boolean hasAccess;
ArrayList<Integer> inChannels;
long latestTimeStamp;
public long getLatestTimeStamp() {
return latestTimeStamp;
}
public void setLatestTimeStamp(long latestTimeStamp) {
this.latestTimeStamp = latestTimeStamp;
}
public ArrayList<Integer> getInChannels() {
return inChannels;
}
public void setInChannels(ArrayList<Integer> inChannels) {
this.inChannels = inChannels;
}
public Long getId() {
return id;
}
public User() {
}
public boolean hasAccess() {
return hasAccess;
}
public void setAccess(boolean hasAccess) {
this.hasAccess = hasAccess;
}
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;
}
}
You can't use #Autowired without one of the annotation that define a component in spring, so in your case, you can use #Service on UserManager like this:
#Service
public class UserManager {
and also don't use static on your method, you have to inject the UserManager component in the controller, as you do with your repository:
#Autowired
private UserManager userManager;
Then you can use:
user = userManager.getUserByAuthentication(onlineRequest.getUsername(), onlineRequest.getPassword());
^^^^^^^^^^^
I fixed it by adding the find function into the repository interface.
Repository
*As I am trying to enter details of user but i am getting bean Exception i don't what i have missed
from repository interface i have implements JPA and i triad with crud as well . I dint mention any controller class yet
public interface UserRepository extends JpaRepository<User,Long> {
public User findByName(String username);
}
UserModel
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String username;
private String email;
private Date DOB;
private String Address;
public User() {
}
public User(Long id, String username, String email, Date DOB, String address) {
this.id = id;
this.username = username;
this.email = email;
this.DOB = DOB;
Address = address;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getDOB() {
return DOB;
}
public void setDOB(Date DOB) {
this.DOB = DOB;
}
public String getAddress() {
return Address;
}
public void setAddress(String address) {
Address = address;
}
}
UserService
Userservice i have given
public interface UserService {
//Creating User
public User CreateUser(User user) throws Exception;
}
ServiceImp
public class UserServiceImp implements UserService {
#Autowired
private UserRepository userData;
//Creating user
#Override
public User CreateUser(User user) throws Exception {
User local=this.userData.findByName(user.getUsername());
if(local!=null)
{
System.out.println("User is already present!!");
throw new Exception("User is already there");
}
else {
local=this.userData.save(user);
}
return local;
}
}
Main Class
This is the main class
#SpringBootApplication
public class UserInformationApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(UserInformationApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
System.out.println("starting Application");
}
}
Changing this
Use for Spring Version 2.x :
import javax.persistence.*;
to
Use for Spring Version 3.x :
import jakarta.persistence.*;
Worked for me
I am making small web-store on Java for university project using Spring Boot and WebLogic server. I don`t use Hibernate, only JDBC.
In controller I need to get my current Spring Security User. I made CustomUser that implements UserDetails class and UserDetailsService implementation.
When I login into the server it accepts my credentials, page reloads, but immediately forwards me back to login page. It should redirect me just to /rootPath/ instead.
Here is my Security Config class
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsServiceImpl userDetailsService;
#Bean
public PasswordEncoder getEncoder(){
return NoOpPasswordEncoder.getInstance();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/registration").not().fullyAuthenticated()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/", "/store/**").hasAnyRole("ADMIN", "USER")
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll()
.and()
.logout()
.permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
super.configure(auth);
}
}
Custom User implementation
public class CustomUser implements UserDetails {
private int id = -1;
private String name;
private String surname;
private String phone;
private String email;
private String password;
private String role;
private ProductCart productCart;
public CustomUser(int id, String name, String surname, String phone, String email, String password, String role) {
this.id = id;
this.name = name;
this.surname = surname;
this.phone = phone;
this.email = email;
this.password = password;
this.role = role;
}
public String getEmail() {
return email;
}
#Override
public Collection<GrantedAuthority> getAuthorities() {
GrantedAuthority authority = new SimpleGrantedAuthority(role);
return Collections.singletonList(authority);
}
#Override
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return email;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
public String getRole() {
return role;
}
}
I tried to permit all requests to my /rootPath/ and get my User directly from Security Context and it showed me anonymousUser, when I login and was redirected to /rootPath/. I think that for some reason Spring or forgets my credentials, or just don't use my custom user as SecurityContext user and still use standart Spring user (for which I don't provide any service). I know for sure that my user loads from database correctly.
Here is my CustomUser class and UserDetailsService implementation (I removed getters and setters)
CustomUser.java
public class CustomUser implements UserDetails {
private int id = -1;
private String name;
private String surname;
private String phone;
private String email;
private String password;
private String role;
private ProductCart productCart;
public CustomUser(int id, String name, String surname, String phone, String email, String password, String role) {
this.id = id;
this.name = name;
this.surname = surname;
this.phone = phone;
this.email = email;
this.password = password;
this.role = role;
productCart = new ProductCart();
}
#Override
public Collection<GrantedAuthority> getAuthorities() {
GrantedAuthority authority = new SimpleGrantedAuthority(role);
return Collections.singletonList(authority);
}
#Override
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return email;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
UserDetailsServiceImpl.java
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
UserDao userDao;
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
CustomUser customUser = userDao.getByEmail(email);
if (customUser == null) {
throw new UsernameNotFoundException("No user found with email: " + email);
}
return customUser;
}
}
Maybe I forgot to configure something or just my realization is wrong in some way?
You are auto-wiring the PasswordEncoder but where is the bean ? Spring is expecting a bean for auto-wiring of type PasswordEncoder
Specify the bean in your configuration file - SecurityConfig :
#Bean
public PasswordEncoder getEncoder(){
return NoOpPasswordEncoder.getInstance();
}
You have to populate the SecurityContextHolder with the principal information you have fetched. For instance, you can extend OncePerRequestFilter and add the following:
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
final SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
I am writing a Spring-MVC application using Spring Boot and Hibernate. The application has a User who has the role of Admin and Customer. I decided to add Spring Security JWT and did everything right. Now that I have everything ready, I want to get a TOKEN, I log in successfully, but in return I get 403 Forbidden. What have I done wrong? Did you configure configs incorrectly or did the roles incorrectly?
Configs:
#RequiredArgsConstructor
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// Fields
//
private final JwtTokenProvider jwtTokenProvider;
//
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().disable().csrf().disable()
.httpBasic().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/auth/login").permitAll()
.anyRequest().authenticated()
.and()
.apply(new JwtConfigurer(jwtTokenProvider));
}
}
Controller:
#RequiredArgsConstructor
#RestController
#RequestMapping("/auth")
public class AuthenticationController {
// Fields
//
private final AuthenticationManager authenticationManager;
private final JwtTokenProvider jwtTokenProvider;
private final UserService userService;
//
// GET-Methods
//
//
#PostMapping("/login")
public ResponseEntity<Map<String, String>> login(#RequestBody AuthenticationRequestDTO requestDto) {
try {
String login = requestDto.getLogin();
authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(login, requestDto.getPassword()));
User user = userService.findByLogin(login);
String token = jwtTokenProvider.createToken(login, user.getRole());
Map<String, String> response = new HashMap<>();
response.put("login", login);
response.put("token", token);
return ResponseEntity.ok(response);
} catch (AuthenticationException e) {
throw new BadCredentialsException("Invalid login or password");
}
}
}
User class:
#Getter
#Entity
#NoArgsConstructor
#Table(name = "users")
public class User {
// Fields
//
#Id
#GeneratedValue
private Long id;
private String name;
#Column(name = "last_name")
private String lastName;
private String login;
private String password;
private String mail;
private boolean isDeleted;
#Enumerated(EnumType.STRING)
private Role role;
}
#RequiredArgsConstructor
#Component
public class JwtTokenProvider {
// Fields
//
private final UserDetailsService userDetailsService;
#Value("${jwt.token.secret}")
private String secret;
#Value("${jwt.token.expired}")
private Long validityInMilliSeconds;
//
// METHODS
//
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(8);
}
#PostConstruct
protected void init() {
secret = Base64.getEncoder().encodeToString(secret.getBytes());
}
/**
* Генерируем ТОКЕН
*
* #param login
* #param role
* #return ТОКЕН
*/
public String createToken(String login, Role role) {
Claims claims = Jwts.claims().setSubject(login);
claims.put("roles", getRoleName(role));
Date now = new Date();
Date validity = new Date(now.getTime() + validityInMilliSeconds);
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(now)
.setExpiration(validity)
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
}
public Authentication getAuthentication(String token) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(getLogin(token));
return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}
public String getLogin(String token) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody().getSubject();
}
public boolean validateToken(String token) {
try {
Jws<Claims> claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
if (claims.getBody().getExpiration().before(new Date())) {
return false;
}
return true;
} catch (JwtException | IllegalArgumentException e) {
throw new JwtAuthenticationException("JWT token is expired or invalid");
}
}
public String resolveToken(HttpServletRequest req) {
String bearerToken = req.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer_")) {
return bearerToken.substring(7, bearerToken.length());
}
return null;
}
private String getRoleName(Role role) {
String roleName = role.name();
return roleName;
}
}
#RequiredArgsConstructor
public class JwtTokenFilter extends GenericFilterBean {
private final JwtTokenProvider jwtTokenProvider;
#Override
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain) throws IOException, ServletException {
String token = jwtTokenProvider.resolveToken((HttpServletRequest) request);
if (token != null && jwtTokenProvider.validateToken(token)) {
Authentication auth = jwtTokenProvider.getAuthentication(token);
if (auth != null) {
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
chain.doFilter(request, response);
}
}
#RequiredArgsConstructor
#Service
#Slf4j
public class JwtUserDetailsService implements UserDetailsService {
private final UserService userService;
#Override
public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException {
User user = userService.findByLogin(login);
JwtUser jwtUser = JwtUserFactory.create(user);
log.info("IN loadUserByUsername - user with login: {} successfully loaded", login);
return jwtUser;
}
}
#AllArgsConstructor
#Getter
public class JwtUser implements UserDetails {
// Fields
//
private Long id;
private String name;
private String lastName;
private String login;
private String password;
private String mail;
private boolean isDeleted;
private Role role;
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role));
}
#Override
public String getUsername() {
return null;
}
#Override
public boolean isAccountNonExpired() {
return false;
}
#Override
public boolean isAccountNonLocked() {
return false;
}
#Override
public boolean isCredentialsNonExpired() {
return false;
}
#Override
public boolean isEnabled() {
return false;
}
}
P.S. I will not throw all the code for JWT from the project here, but skip only the most important thing.
LOGS:
2020-08-10 16:36:27.343 INFO 7556 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Completed initialization in 64 ms
2020-08-10 16:36:28.454 INFO 7556 --- [nio-8080-exec-2] r.c.security.JwtUserDetailsService : IN loadUserByUsername - user with login: art123 successfully loaded
There in Spring security 5 appear an interface as ReactiveUserDetailsService.
Now my question is how to implement UserDetailsService using:
Spring-data-Mongo
Spring Security (5)
That I have (users stored in memory)
#Bean
public MapReactiveUserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder().username("test").password("password").roles("USER").build();
UserDetails admin = User.withDefaultPasswordEncoder().username("admin").password("admin").roles("USER", "ADMIN").build();
return new MapReactiveUserDetailsService(user, admin);
}
What I want:
#Document(collection = "user")
public class User implements UserDetails {
#Id
private Long id;
private LocalDate createdAt;
private String username;
private String password;
private boolean accountNonLocker;
private boolean enabled;
#DBRef
private List<GrantedAuthority> grantedAuthorities;
//getters and setters
}
#Component
public class SecUserDetailsService implements ReactiveUserDetailsService {
}
And I don't know how to implement it. not found any resources on web.
Already found a solution:
Implement UserDetails interface in UseAccount class and create Role class that implements GrantedAuthority interface:
Role
#Data
#Document
public class Role implements GrantedAuthority {
#Id
private String id;
#Override
public String getAuthority() {
return id;
}
}
UseAcount
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Document
public class UserAccount implements UserDetails {
#Id
private String id;
private String username;
private String password;
private String firstName;
private String lastName;
private String idnp;
#Email
private String email;
#Builder.Default()
private boolean active = true;
#Builder.Default()
#DBRef
private List<Role> roles = new ArrayList<>();
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roles;
}
#Override
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return username;
}
#Override
public boolean isAccountNonExpired() {
return active;
}
#Override
public boolean isAccountNonLocked() {
return active;
}
#Override
public boolean isCredentialsNonExpired() {
return active;
}
#Override
public boolean isEnabled() {
return active;
}
#Override
public String getName() {
return firstName + " " + lastName;
}
}
Define Security configuration and in it create web filter.
#Configuration
#EnableWebFluxSecurity
public class SecurityConfiguration {
#Bean
SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) {
return http
.csrf().disable()
.authorizeExchange()
.pathMatchers("/login", "/logout").permitAll()
.pathMatchers("/i18n/**",
"/css/**",
"/fonts/**",
"/icons-reference/**",
"/img/**",
"/js/**",
"/vendor/**").permitAll()
.anyExchange()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.and()
.logout()
.logoutUrl("/logout")
.and()
.build();
}
//in case you want to encrypt password
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Create repository to extract your user from database.
And last step is to create UserDetailsService
#Component
public class SecUserDetailsService implements ReactiveUserDetailsService {
#Autowired
public ReactiveUserAccountRepository reactiveUserAccountRepository;
#Override
public Mono<UserDetails> findByUsername(String username) {
Mono<UserAccount> data = reactiveUserAccountRepository.findByUsername(username);
return data.cast(UserDetails.class);
}
}