Get username in session Spring Security - spring

I am in doubt as to how to get the user name in the session. I am using Spring Security 4.2
I have my Class Usuario
#Entity
#Data
public class Usuario {
#Id #GeneratedValue
private Integer id;
private String login;
private String senha;
private String papel;
}
My class UsuarioController
#Named
#ViewScoped
public class UsuarioController {
#Autowired
private UsuarioRepository usuarioRepository;
#Getter #Setter
private List<Usuario> usuarios;
#Getter #Setter
private Usuario usuario = new Usuario();
}
And my class SecurityConfig, which plays the role of the filter, already built into Spring Security.
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UsuarioRepository usuarioRepository;
#Override
protected void configure(HttpSecurity http) {
try {
http.csrf().disable();
http
.userDetailsService(userDetailsService())
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/cliente.jsf").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.jsf")
.permitAll()
.failureUrl("/login.jsf?error=true")
.defaultSuccessUrl("/cliente.jsf")
.and()
.logout()
.logoutSuccessUrl("/login.jsf");
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
#Override
protected UserDetailsService userDetailsService() {
List<Usuario> usuarios = usuarioRepository.findAll();
List<UserDetails> users = new ArrayList<>();
for(Usuario u: usuarios){
UserDetails user = new User(u.getLogin(), u.getSenha(), AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_"+u.getPapel()));
users.add(user);
} return new InMemoryUserDetailsManager(users);
}
}
I already researched other posts in the forum, did not help, any tips? Do I need to create another class?

If you want to get the username of the current user authenticated with Spring Security, you could use the following:
final String currentUserName = SecurityContextHolder.getContext().getAuthentication().getName();
Here, we find the current Authentication and query it for the username. For password-based authentiction, getName() returns user's login.

You can create your own SecurityUtility class like this:
public final class SecurityUtils {
private SecurityUtils() {
}
public static String getUserName() {
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
String userName = null;
if (authentication != null) {
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
userName = userDetails.getUsername();
}
return userName;
}
And call it from the class where you need the username, for example: SecurityUtils.getCurrentUserLogin();

Related

Spring Security cannot authenticate user although the user is exist in database

please help me with this, I'm new to spring security and I have been trying to logged in but Spring Security just don't let me access and I still can't figure. My CustomUserDetailsService still working and print out the account I intend to use to login
SecurityConfig
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
BCryptPasswordEncoder passwordEncoder;
#Bean
#Override
protected AuthenticationManager authenticationManager() throws Exception {
// TODO Auto-generated method stub
return super.authenticationManager();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/login", "/logout").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.defaultSuccessUrl("/admin", true)
.and()
.exceptionHandling().accessDeniedPage("/accessDenied");
}
}
CustomUserDetailsService
#Service("customUserDetailsService")
#Transactional
#Slf4j
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Autowired
private RoleRepository roleRepository;
#Autowired
private PasswordEncoder passwordEncoder;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findUsersByUsername(username);
if (user == null) {
log.error("User not found");
throw new UsernameNotFoundException("User not found");
} else {
log.info("User found in the dbs", username);
System.out.println(user.getUsername());
System.out.println(user.getPassword());
}
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
//looping all roles from user -> for each role, create a new simpleGranted
//auth by passing the role name
for (Role role : user.getRoles()) {
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
//return spring sec user (core userDetail)
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
}
}
User
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "user",
uniqueConstraints = {
#UniqueConstraint(columnNames = "username"),
#UniqueConstraint(columnNames = "email")
})
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank(message = "Username is required")
private String username;
#NotBlank(message = "Password is required")
private String password;
#Email
#NotBlank(message = "Email is required")
private String email;
private Instant created;
private boolean enabled;
//load all the roles whenever load an user
#ManyToMany(fetch = FetchType.EAGER)
private Collection<Role> roles = new ArrayList<>();
}
Everytime I logged in with the right account, Spring Security always give me "Bad Credentials"
Edited: username and password (both passwords are 123)
It isn't working because you didn't specify the implementation of the UserDetailsService.So what spring is actually doing is, It is using the default username(user) and the random password(generated at runtime) as the required credentials. To make spring use your custom user details, please replace
This:
#Autowired
private UserDetailsService userDetailsService;
With that:
#Autowired
#Qualifier("customUserDetailsService")
private UserDetailsService userDetailsService;

how get authfication user from PostMapping method controller

I can't get authfication user from post request method in controller. I am tryed use #AuthficationPrincipal UserDetails, Principal and SecurityContextHolder but his returns null. It's need me for upload images to datebase. Help me solve this problem please. (.csrf disabled)
Controller:
#Controller
#RequestMapping("/images")
public class ImageController {
private final ImageService imageService;
private final UserService userService;
#Autowired
public ImageController(ImageService imageService,
UserService userService) {
this.imageService = imageService;
this.userService = userService;
}
#PostMapping("/load-image")
public String loadImage(#RequestParam("image") MultipartFile image,
#AuthenticationPrincipal UserDetails user){
User authUser = userService.findUserByNickname(user.getUsername());
imageService.load(image, authUser);
return "redirect:/users/show/"+authUser.getId();
}
}
Security config:
#Configuration
#EnableWebSecurity
public class SecurityCFG extends WebSecurityConfigurerAdapter {
private final BCryptPasswordEncoder bCryptPasswordEncoder;
private final MyUserDetailsService userDetailsService;
#Autowired
public SecurityCFG(BCryptPasswordEncoder bCryptPasswordEncoder,
MyUserDetailsService userDetailsService) {
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
this.userDetailsService = userDetailsService;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
csrf().disable()
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/orders/**").authenticated()
.antMatchers("/users/orders").authenticated()
.antMatchers("/admin/**").hasRole("ADMIN")
.and()
.formLogin().loginPage("/users/login")
.usernameParameter("login")
.passwordParameter("password")
.and()
.logout().logoutSuccessUrl("/users/login?logout").permitAll();
}
}
UserDetails Service:
#Service
public class MyUserDetailsService implements UserDetailsService {
private final UserService userService;
#Autowired
public MyUserDetailsService(UserService userService) {
this.userService = userService;
}
#Override
#Transactional
public UserDetails loadUserByUsername(final String login){
User user;
if(login.contains("#")){
user = userService.findUserByEmail(login);
}else{
user = userService.findUserByNickname(login);
}
if(user!=null){
List<GrantedAuthority> authorities = getUserAuthority(user.getRoles());
return buildUserForAuthentication(user, authorities);
}
throw new BadCredentialsException(String.format("Логин %s неверный",login));
}
private List<GrantedAuthority> getUserAuthority(Set<Role> userRoles) {
Set<GrantedAuthority> roles = new HashSet<>();
for (Role role : userRoles) {
roles.add(new SimpleGrantedAuthority(role.getRole()));
}
return new ArrayList<>(roles);
}
private UserDetails buildUserForAuthentication(User user,
List<GrantedAuthority> authorities) {
UserDetails userDetails = new
org.springframework.security.core.userdetails.User(user.getNickname(),
user.getPassword(),user.isActive(), true,true,
user.isAccountNonLocked(), authorities);
new AccountStatusUserDetailsChecker().check(userDetails);
return userDetails;
}
}
Its because you are using #Controller and not #RestController
If you want to get your controller to work properly you should be using #RestController instead of only #Controller on your rest controller classes. #RestController is actually a shorthand for #Controller and #ResponseBody which basically tells spring that you want to serialize all responses from functions to something like json, or xml etc. etc.
you can read more about the annotation here.
Removing #RequestMapping("/images") from the controller fixed this problem, but I don't understand why this is happening.

How to get a username from post method in spring security?

I am using spring-boot and spring-security in app. My goal is to get the user name of the currently registered user from post method. Get method is working nicely but the post method isn't working. Why? How can I solve this problem?
Test Controller
#GetMapping("/test")
public String test(Authentication authentication) {
System.out.println(authentication.getName()); // <--------- It's working
return "testfile";
}
#PostMapping("/test")
public String testPost(Authentication authentication) {
System.out.println(authentication.getName()); // <--------- NOLL ERROR!
return "testfile";
}
Error
java.lang.NullPointerException: null
User
#Entity
#Table(name="user")
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private long id;
#Column(name="mail")
private String mail;
#Column(name="password")
private String password;
}
UserDAO
#Repository
public class UserDAO {
#Autowired
private EntityManager entityManager;
public List<User> findAll() {
return entityManager.unwrap(Session.class).createQuery("from User", User.class).getResultList();
}
public User findByMail(String mail){
Session currentSession = entityManager.unwrap(Session.class);
Query theQuery = currentSession.createQuery("from User where mail=:mail", User.class);
theQuery.setParameter("mail", mail);
List<User> users = theQuery.getResultList();
if(users.isEmpty()){
return new User();
}
return users.get(0);
}
public void saveOrUpdate(User user) {
Session currentSession = entityManager.unwrap(Session.class);
currentSession.saveOrUpdate(user);
}
}
UserService
public interface UserService extends UserDetailsService{
public List<User> findAll();
public User findByMail(String mail);
public void saveOrUpdate(User user);
}
UserServiceImpl
#Service
public class UserServiceImpl implements UserService{
#Autowired
private UserDAO userDAO;
#Autowired
private UserRoleDAO userRoleDAO;
#Autowired
private BCryptPasswordEncoder passwordEncoder;
#Override
#Transactional
public List<User> findAll() {
return userDAO.findAll();
}
#Override
#Transactional
public User findByMail(String mail){
return userDAO.findByMail(mail);
}
#Override
#Transactional
public void saveOrUpdate(User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
userDAO.saveOrUpdate(user);
}
#Override
#Transactional
public UserDetails loadUserByUsername(String mail) throws UsernameNotFoundException {
User user = userDAO.findByMail(mail);
List<UserRole> userRole = userRoleDAO.findByUserId(user.getId());
if (user == null) {
throw new UsernameNotFoundException("Invalid username or password.");
}
return new org.springframework.security.core.userdetails.User(user.getName(), user.getPassword(), mapRolesToAuthorities(userRole));
}
private Collection<? extends GrantedAuthority> mapRolesToAuthorities(Collection<UserRole> roles) {
return roles.stream().map(role -> new SimpleGrantedAuthority(role.getRole())).collect(Collectors.toList());
}
}
SecurityConfig
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private Environment env;
#Autowired
private DataSource dataSource;
#Autowired
private UserService userService;
RedirectAuthenticationSuccessHandler redirectAuthenticationSuccessHandler = new RedirectAuthenticationSuccessHandler();
RedirectAuthenticationFailureHandler redirectAuthenticationFailureHandler = new RedirectAuthenticationFailureHandler();
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery(env.getProperty("my.usersbyusernamequery"))
.authoritiesByUsernameQuery(env.getProperty("my.authoritiesbyusernamequery"));
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/anypage1/**").hasRole("MANAGER")
.antMatchers("/anypage2/**").hasRole("ADMIN")
.antMatchers("/test").hasRole("ADMIN")
.authenticated()
.antMatchers("/**").permitAll()
.and()
.formLogin().loginPage("/login").failureHandler(redirectAuthenticationFailureHandler)
.loginProcessingUrl("/login-control").successHandler(redirectAuthenticationSuccessHandler).permitAll()
.and()
.logout().logoutUrl("/logout").permitAll().and().exceptionHandling().accessDeniedPage("/access-denied");
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.POST, "/anypage3").antMatchers(HttpMethod.POST, "/anypage4")
.antMatchers(HttpMethod.POST, "/test");
}
#Bean
public BCryptPasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
auth.setUserDetailsService(userService);
auth.setPasswordEncoder(passwordEncoder());
return auth;
}
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.POST, "/anypage3").antMatchers(HttpMethod.POST, "/anypage4")
.antMatchers(HttpMethod.POST, "/test");
}
You ignore /test in post method, so it will not be filtered by spring security filter, try to remove this.
You can get username from SecurityContextHolder
User user =
(User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String name = user.getUsername(); //get current logged in username
In loadUserByUsername method you can manually set the Authentication token on SecurityContextHolder and same you can use in controller
UsernamePasswordWithAttributesAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( loadUserByUsername(username), password, authorities );
SecurityContextHolder.getContext().setAuthentication(authenticationToken);

String Boot REST security Error : type=Forbidden, status=403

This project I have used Spring boot, security authentication, JPA and REST
This gives me 403 error, which is of role base error. I have tried for 2 days could not solve please help.
Here I am sharing code.
This is my Security config class have roles based /hello for all user and /admin/all and /admin/add for admin user.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(encodePsw());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().antMatchers("/api/secure/admin/**").hasRole("ADMIN").antMatchers("/api/secure/**")
.hasAnyRole("USER", "ADMIN").anyRequest().authenticated().and().formLogin().permitAll();
}
#Bean
public BCryptPasswordEncoder encodePsw() {
return new BCryptPasswordEncoder();
}
}
This is my Controller class have 3 method which is or 3 role based /hello for all user and /admin/all and /admin/add for admin user.
#RestController
#RequestMapping("/api/secure")
public class AdminController {
#Autowired
private UserRepository userRepo;
#Autowired
private BCryptPasswordEncoder passEncp;
#RequestMapping(value = "/hello")
public String hello() {
return "Hello..";
}
//#PreAuthorize("hasAnyRole('ADMIN')")
#RequestMapping(value = "/admin/add", method = RequestMethod.POST)
public String addUserByAdmin(#RequestBody User user) {
user.setPassword(passEncp.encode(user.getPassword()));
userRepo.save(user);
return "Add User Successfully";
}
//#PreAuthorize("hasAnyRole('ADMIN')")
#GetMapping("/admin/all")
public String securedHello() {
return "Secured Hello";
}
}
This is Role bean
#Entity
#Data
public class Role {
#Id
#GeneratedValue
private int roleId;
private String role;
}
This User Bean
#Entity
#Setter
#Getter
public class User {
#Id
private int userId;
private String username;
private String password;
private String email;
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#JoinTable(name="user_role", joinColumns = #JoinColumn(name="user_id"), inverseJoinColumns = #JoinColumn(name="role_id"))
private Set<Role> roles;
}
UserRepository interface
public interface UserRepository extends JpaRepository<User, Integer> {
User findByUsername(String username);
}
CustomUserDetails class
I think the problem in this part. I have save role like ROLE_USER, ROLE_ADMIN also tried without ROLE_
#Getter
#Setter
public class CustomUserDetails implements UserDetails {
private User user;
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getRoles().stream().map(role -> new SimpleGrantedAuthority(""+role))
.collect(Collectors.toList());
}
#Override
public String getPassword() {
// TODO Auto -generated method stub
return user.getPassword();
}
#Override
public String getUsername() {
// TODO Auto-generated method stub
return user.getUsername();
}
Service class CustomUserDetailsService
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepo;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepo.findByUsername(username);
CustomUserDetails userDetails = null;
if(user!=null) {
userDetails = new CustomUserDetails();
userDetails.setUser(user);
}else {
throw new UsernameNotFoundException("User not found with name "+username);
}
return userDetails;
}
}
I have another Controller class having different URL mapping which is running
WebSecurityConfigurerAdapter has a overloaded configure message that takes WebSecurity as argument which accepts ant matchers on requests to be ignored.
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/hello");
}
You can ignore /hello based url as its for all users.
Actual:
return user.getRoles().stream().map(role->new SimpleGrantedAuthority("ROLE_"+role)).collect(Collectors.toList());
Expected:
return user.getRoles().stream().map(role->new SimpleGrantedAuthority("ROLE_"+role.getRole_name())).collect(Collectors.toList());

Spring Security + MongoDB, request get 401 all the time

I am learning how to use Spring and how to implement Spring Security in my project, using Roles and MongoDB.
I have a User model with a role list, and I want to let only ADMIN user to use some endpints from Controller.
Here is the User class and Role enum:
#Document(collection = "Users")
public class User {
#Id
private String id;
private String firstName;
private String lastName;
private String email;
private String password;
private List<Role> roles;
//constructor + getters and setters
}
public enum Role {
ADMIN,
BASIC_USER
}
I use the this UserDetails implementation: I think here is the problem...
public class CustomUserDetails implements UserDetails {
private User user;
public CustomUserDetails() {
}
public CustomUserDetails(User user) {
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.user.getRoles().stream().map(role -> new SimpleGrantedAuthority(String.format("ROLE_%s", role))).collect(Collectors.toList());
}
#Override
public String getPassword() {
return this.user.getPassword();
}
#Override
public String getUsername() {
return this.user.getEmail();
}
#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 User getUser() {
return user;
}
}
The UserDetailsService looks like this:
public class CustomUserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
final User user = userRepository.findByEmail(email);
if (Objects.isNull(user)) {
throw new UsernameNotFoundException("User not found");
}
return new CustomUserDetails(user);
}
}
I made this configuration:
#Configuration
#EnableWebSecurity(debug = true)
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(createUserDetailsService()).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/users/**").permitAll()
.antMatchers( "/users/**").authenticated().anyRequest().hasAnyRole("ADMIN")
.antMatchers("/users/me").authenticated().anyRequest().hasAnyRole("ADMIN", "BASIC_USER")
.antMatchers( "/users/**").permitAll()
.and()
.formLogin().disable()
.httpBasic();
}
#Override
public void configure(WebSecurity web) throws Exception {
/* To allow Pre-flight [OPTIONS] request from browser */
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
}
/**
* Create password encoder bean used for encrypting the password
*
* #return
*/
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* Create user service bean used for find the user by email
* #return
*/
#Bean
public UserDetailsService createUserDetailsService() {
return new CustomUserDetailsServiceImpl();
}
}
When I make any call with Postman at localhost:8080/users using Basic auth with the admin details from DB, all the time I get 401 Unauthorized Status
I think the problem is that I use an Enum for Roles and I don't know how to correctly build UserDetails implementation.
If helps, this is the UserController
#RestController
#RequestMapping("/users")
public class UserController {
#Autowired
private UserService userService;
#GetMapping(path = "")
public List<User> getUsers(){
return this.userService.getUsers();
}
}
When I used MySQL for this project (With the same classes for security) the apps worked perfectly. In that implementation I used Roles, Users and Users_Roles tables.

Resources