Extends Spring security user class - spring

I'm Working on a Spring security project . I try to extends the security.core.userdetails.User class to add more details while registering the users.
User Extended class
public class UserDetails extends User {
private int id;
private String Country;
public UserDetails(String username, String password, Collection<? extends GrantedAuthority> authorities, int id,
String country) {
super(username, password, authorities);
this.id = id;
Country = country;
}
public UserDetails(String username, String password, boolean enabled, boolean accountNonExpired,
boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities,
int id, String country) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
this.id = id;
Country = country;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCountry() {
return Country;
}
public void setCountry(String country) {
Country = country;
}
I have also added Id and country in my entity class(model class).
But when i try to register the user .
It give an error.org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [insert into users (username, password, enabled) values (?,?,?)]; Field 'id' doesn't have a default value; nested exception is java.sql.SQLException: Field 'id' doesn't have a default value
(The value of id and country is hard coded)
Controller class
try {
List<GrantedAuthority> authority = new ArrayList<>();
authority.add(new SimpleGrantedAuthority(form.getRole()));
String encodedPassword = passwordEncoder.encode(form.getPassword());
UserDetails details = new UserDetails(form.getUsername(), encodedPassword, authority, 10 ,"India");
System.out.println(details.getId()+" "+details.getCountry() +" "+details.getUsername());
System.out.println(details);
detailsManager.createUser(details);
}
OUPUT
10 India alpha#gmail.com
com.example.demo.model.UserDetails [Username=alpha#gmail.com, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_ADMIN]]
I don't know why its is calling the parent class constructor.

The SQL is incorrect. Spring Security's INSERT by default populates the username, password, and enabled columns. However, the users table you created requires an id column as well. Since the query doesn't specify the value, it fails.
You could try extending JdbcUserDetailsManager's various methods to be aware of your id field as well. You'd need to at least extend createUser so it adds the id to the INSERT statement and findUserByUsername so it constructs your custom object.
A better way, though, would be to use Spring Data. This allows your domain object to be independent of Spring Security. Also, Spring Data has much broader SQL support.
It might be helpful to call your class something different than a Spring Security interface. So, let's imagine that your custom class (the one with the id) is called YourUser (instead of UserDetails). Now, you can wire a Spring Data-based UserDetailsService to Spring Security like so:
#Service
public class YourUserRepositoryUserDetailsService implements UserDetailsService {
private final YourUserRepository users; // your Spring Data repository
// ... constructor
#Override
public UserDetails findUserByUsername(String username) {
YourUser user = this.users.findByUsername(username)
.orElseThrow(() -> new UserNotFoundException("not found"));
return new UserDetailsAdapter(user);
}
private static class UserDetailsAdapter extends YourUser implements UserDetails {
UserDetailsAdapter(YourUser user) {
super(user); // copy constructor
}
// ... implement UserDetails methods
}
}
This UserDetailsService replaces the JdbcUserDetailsManager that you are publishing.

Related

What is the CLI command to view inside of a set data type in redis

I user a CRUDRepository in my spring data redis project to persist a redis hash in my redis cluster. i have rest api written to persist and get thte values of the data. this works fine.
however my entity annotated with RedisHash is being saved as a set / and i am not able to look inside the value using redis cli.
how do i look inside a set data type(without popping) in redis cli
i looked at redis commands page https://redis.io/commands#set
i only get operations which can pop value . i neeed to simply peek
EDIT:
to make things clearer, i am using spring crudrepo to save the user entity into redis data store. the user entity gets saved as a set data type.
when i query back the user details, i can see entire details of the user
{
userName: "somak",
userSurName: "dattta",
age: 23,
zipCode: "ah56h"
}
i essentially want to do the same using redis cli... but all i get is
127.0.0.1:6379> smembers user
1) "somak"
how do i look inside the somak object.
#RestController
#RequestMapping("/immem/core/user")
public class UserController {
#Autowired
private UserRepository userRepository;
#RequestMapping(path = "/save", method = RequestMethod.GET, produces = "application/json")
#ResponseStatus(HttpStatus.OK)
public void saveUserDetails() {
User user = new User();
user.setAge(23);
user.setUserName("somak");
user.setUserSurName("dattta");
user.setZipCode("ah56h");
userRepository.save(user);
}
#RequestMapping(path="/get/{username}", method = RequestMethod.GET, produces = "application/json")
public User getUserDetails(#PathVariable("username") String userName) {
return userRepository.findById(userName).get();
}
}
#Repository
public interface UserRepository extends CrudRepository<User, String>{
}
#RedisHash("user")
public class User {
private #Id String userName;
private #Indexed String userSurName;
private #Indexed int age;
private String zipCode;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserSurName() {
return userSurName;
}
public void setUserSurName(String userSurName) {
this.userSurName = userSurName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
}
I don't understant your descr with your problem, but I understand your title.
In redis set, the member is always string type.
I hope you can offer more info about UserRepository.save:
User user = new User();
user.setAge(23);
user.setUserName("somak");
user.setUserSurName("dattta");
user.setZipCode("ah56h");
userRepository.save(user);
And you can check your redis data and check data type when rest api invoked.

Display user details in jsp using spring security

I have implemented an app in spring boot with spring security. I need to display User's firstname, lastname and the image path in jsp page (which is used for header, it means globally available even if we navigate to another URL) after successfully logged in. I'm using remember-me using PersistentLogin. So I can't use session to store details. Because If I close the browser, session will be destroyed.
I have implemented CustomUserDetailsService, it returns org.springframework.security.core.userdetails.User
#Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService{
//codes
return new org.springframework.security.core.userdetails.User(
username,
password,
enabled,
accountNonExpired,
credentialsNonExpired,
accountNonLocked,
authorities);
}
I know there are two limitaions
If I don't use remember-me, I can easily store within session.
If I return User model class in CustomUserDetailsService ...,I can easily get user details in jsp
page using<security:authentication property="principal.firstName">
tag in jsp. But I need to return org.springframework.security.core.userdetails.User
Unfortunately I need both limitation. My User model class has firstName, lastName, imagePath,.. etc.
How can I display user details in jsp page? Any approaches available? Thanks in advance.
Spring inbuilt provides the solution to do the same.
Java code :
public User getCurrentUser() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
Object principal = auth.getPrincipal();
if (principal instanceof User) {
return ((User) principal);
}
}
}
JSP code :
${pageContext["request"].userPrincipal.principal}
HWat i have done is, I created a prototype of User called UserAuth
public class UserAuth extends org.springframework.security.core.userdetails.User{
private String firstName;
private String lastName;
private String imagePath;
public UserAuth(String username, String password, boolean enabled,
boolean accountNonExpired, boolean credentialsNonExpired,
boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities,
String firstName, String lastName, String imagePath) {
super(username, password, enabled, accountNonExpired,
credentialsNonExpired, accountNonLocked, authorities);
this.firstName = firstName;
this.lastName = lastName;
this.imagePath = imagePath;
}
//getters and setters
}
In CustomeUserDetailsService
#Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService{
//codes
return UserAuth
username,
password,
enabled,
accountNonExpired,
credentialsNonExpired,
accountNonLocked,
authorities,
user.getFirstName(),
user.getLastName(),
user.getImagePath());
}

Spring Security endpoint protection by dynamic roles

Have a doubt regarding protecting endpoints by user roles. I have a endpoint "/message" it is protected in either of two ways as shown below
1) In Controller as follows
#PreAuthorize("hasAuthority('USER')")
#RequestMapping(value = "/message", method = RequestMethod.GET)
public String message() {
return "Hello World!!!"
}
2) In configuration(WebSecurityConfigurereAdapter) file as follows
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().anyRequest().authenticated()
.antMatchers(HttpMethod.GET, "/message").access("hasAuthority('USER')");
}
As it can be seen role USER has been hard coded in both ways, how can this be achieved dynamically, one way is we can read from database in configuration file and build HttpSecurity, but this happens during start of the application, how can endpoints be protected by new roles created at runtime?
Best way to protect your endpoints that can be changed anytime:
In case if you want to grant/revoke roles/permission from a user, the best approach would be to play with privileges instead of roles. In that case, each user will have just one role. And each role may have a list of privileges, that can be added/removed from a role anytime using UI.
How to do?
public class Privilege{
#Id
private String id;
private String name;
//Constructors + Getters & Setters
}
public class Role{
#Id
private String id;
private String name;
private List<Privilege> privileges;
//Constructors + Getters & Setters
}
public class MyUser{
private Role role;
//All Required params
//Constructors + Getters & Setters
}
public class MyUserDetailsService implements UserDetailsService{
public User loadUserByUsername(final String userId) {
User user = //load your user
Role role = //load above user role
Collection<GrantedAuthority> grantedAuthorities.add(new SimpleGrantedAuthority(merchantRole.getName()));
for(Privilege privilege : role.getPrivileges()){
grantedAuthorities.add(new SimpleGrantedAuthority(privilege.getName()));
}
return new User(username, password, grantedAuthorities);
}
}
And finally your configuration file should look like this:
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().anyRequest().authenticated()
.antMatchers(HttpMethod.GET, "/message").access("hasAuthority('YOUR_PRIVILAGE')");
}
Note
Try to create privileges as indivisible as possible. Mean to say each privilege should be specific to each specific task. So that anytime any role can be created with any combination of privileges without changing antMatchers/security.
Example:
CREATE_USER_PRIVILEGE
UPDATE_USER_PRIVILEGE
DELETE_USER_PRIVILEGE
VIEW_USER_PRIVILEGE
etc.
ADMIN_ROLE = {CREATE_USER_PRIVILEGE, UPDATE_USER_PRIVILEGE, DELETE_USER_PRIVILEGE, VIEW_USER_PRIVILEGE}
ADMIN_ROLE = {VIEW_USER_PRIVILEGE}
etc.

Spring Security LDAP get User Given Name

I am using Spring security 3.2.4 with Windows AD LDAP.
I am able to successfully authenticate and LdapUserDetailsImpl is populated.
From LdapUserDetailsImpl I can get the username, authorities, but how to get the employee name (not the login user name)
LdapUserDetailsImpl contains following properties and values
Username = 40000 ,
Enabled = true,
AccountNonExpired = true,
Dn: cn=employee name,ou=IT_FM,ou=XXX_USERS,dc=XXXX,dc=CO,dc=IN;
How do it get the employee name, Do I need to extend some class and write my own mapping or
may be simply get Dn from the principal and split the string to get the employee name.
You can just get the Dn from Principal and extract the username (cn)
LdapUserDetailsImpl ldapDetails = (LdapUserDetailsImpl) SecurityContextHolder
.getContext().getAuthentication().getPrincipal();
String dn = ldapDetails.getDn();
int beginIndex = dn.indexOf("cn=") + 3;
int endIndex = dn.indexOf(",");
String username = dn.substring(beginIndex, endIndex);
#Mukun almost has this. The only thing is, instead of:
String dn = ldapUserDetailsImpl.getDn();
int beginIndex = dn.indexOf("cn=") + 3;
int endIndex = dn.indexOf(",");
myUserDetails.setEmployeeName(dn.substring(beginIndex, endIndex));
I would have:
String name = ctx.getObjectAttribute("cn").toString()
myUserDetails.setEmployeeName(name)
This lets LDAP integration handle all the horrible stuff for you and loses the danger of chopping up strings yourself.
You might also consider
myUserDetails.setFirstName(ctx.getObjectAttribute("givenName").toString())
myUserDetails.setLastName(ctx.getObjectAttribute("sn").toString())
These things should work for both MS AD, "normal" LDAP and possible Novell too.
So the full answer would be:
#Service
public class MyUserDetailsContextMapper extends LdapUserDetailsMapper implements UserDetailsContextMapper {
#Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
LdapUserDetailsImpl ldapUserDetailsImpl = (LdapUserDetailsImpl) super.mapUserFromContext(ctx, username, authorities);
MyUserDetails myUserDetails = new MyUserDetails();
myUserDetails.setAccountNonExpired(ldapUserDetailsImpl.isAccountNonExpired());
myUserDetails.setAccountNonLocked(ldapUserDetailsImpl.isAccountNonLocked());
myUserDetails.setCredentialsNonExpired(ldapUserDetailsImpl.isCredentialsNonExpired());
myUserDetails.setEnabled(ldapUserDetailsImpl.isEnabled());
myUserDetails.setUsername(ldapUserDetailsImpl.getUsername());
myUserDetails.setAuthorities(ldapUserDetailsImpl.getAuthorities());
myUserDetails.setEmployeeName(ctx.getObjectAttribute("cn").toString());
return myUserDetails;
}
}
My Custom Mapper. Is this correct way of doing ?
#Service
public class MyUserDetailsContextMapper extends LdapUserDetailsMapper implements UserDetailsContextMapper {
#Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
LdapUserDetailsImpl ldapUserDetailsImpl = (LdapUserDetailsImpl) super.mapUserFromContext(ctx, username, authorities);
MyUserDetails myUserDetails = new MyUserDetails();
myUserDetails.setAccountNonExpired(ldapUserDetailsImpl.isAccountNonExpired());
myUserDetails.setAccountNonLocked(ldapUserDetailsImpl.isAccountNonLocked());
myUserDetails.setCredentialsNonExpired(ldapUserDetailsImpl.isCredentialsNonExpired());
myUserDetails.setEnabled(ldapUserDetailsImpl.isEnabled());
myUserDetails.setUsername(ldapUserDetailsImpl.getUsername());
myUserDetails.setAuthorities(ldapUserDetailsImpl.getAuthorities());
String dn = ldapUserDetailsImpl.getDn();
int beginIndex = dn.indexOf("cn=") + 3;
int endIndex = dn.indexOf(",");
myUserDetails.setEmployeeName(dn.substring(beginIndex, endIndex));
return myUserDetails;
}
}

Spring roo and One-To-Many relationship in GUI generation

I cannot generate an appropriate GUI via roo for a one-to-many relationship. In particular, I would need a multiple choice element to select among the authorities (spring security) to associate to the user.
I created my RegisteredUser class:
#RooJavaBean
#RooToString
#RooJpaActiveRecord
public class RegisteredUser extends MyUser implements UserDetails,
CredentialsContainer {
private String password;
private String username;
private Boolean enabled = true;
private Boolean accountNonExpired = true;
private Boolean credentialsNonExpired = true;
private Boolean accountNonLocked = true;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<MyBaseAuthority> authorities = new HashSet<MyBaseAuthority>();
#Override
public void eraseCredentials() {
password = null;
}
#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 accountNonExpired;
}
#Override
public boolean isAccountNonLocked() {
return accountNonLocked;
}
#Override
public boolean isCredentialsNonExpired() {
return credentialsNonExpired;
}
#Override
public boolean isEnabled() {
return enabled;
}
}
Then MyBaseAuthority class:
#RooJavaBean
#RooToString
#RooJpaActiveRecord
public class MyBaseAuthority extends ObjectWithId implements
GrantedAuthority {
private String authority;
#Override
public String getAuthority() {
return authority;
}
}
Then I had to manually create the controller for MyBaseAuthority, but not for RegisteredUser (generated by webmvc command):
#RequestMapping("/registeredusers")
#Controller
#RooWebScaffold(path = "registeredusers", formBackingObject = RegisteredUser.class)
public class RegisteredUserController {
}
#RequestMapping("/authorities")
#Controller
#RooWebScaffold(path = "authorities", formBackingObject = MyBaseAuthority.class)
public class MyBaseAuthorityController {
}
On the GUI, I can create and list all authorities and registered users. However, when creating a registered user, I can only set string fields and boolean fields, but not the one-to-many relationship. How can I fix that?
If I were trying to acomplish this task I would print out all of my checkboxes with the available options as array keys with a name like so:
<input type="checkbox" name="role[]" value="ROLE_ONE">
<input type="checkbox" name="role[]" value="ROLE_TWO">
Then, I would map these parameters to a String[] array like in this post
#RequestParam(value="myParam[]" String roles)
I would then loop over the strings and add create the MyBaseAuthority objects, attach your user and persist() them.

Resources