Match password efficiently in Spring Boot - JPA - json

I have User class like this :
#Data
#Entity
public class User {
#Id #GeneratedValue Long userID;
String eMail;
String passwordHash;
}
And I have data like this :
[{"userID":1,"passwordHash":"asdasd","email":"admin#admin.com"},
{"userID":2,"passwordHash":"12345","email":"admin1asdasd#admin.com"}]
I have two method , one - to get single user :
// Single item
#GetMapping("/user/{id}")
User one(#PathVariable Long id) {
return repository.findById(id)
.orElseThrow(() -> new UserNotFoundException(id));
}
Other method to retrieve all user :
// Aggregate root
#GetMapping("/user")
List<User> all() {
return repository.findAll();
}
Now how can I match password ? What will be the efficient way ?

You may want to consider this kind of an aproach: in general, you should save hashed password in the database and check passwords using hashed values. Bcrypt is a good option for hashing and it can be easily integrated with Spring.
As explained in the link above you can define a password encoder service:
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
and you can use it like this:
#Autowired
private PasswordEncoder passwordEncoder;
//...
User user = new User();
user.setFirstName(accountDto.getFirstName());
user.setLastName(accountDto.getLastName());
user.setPassword(passwordEncoder.encode(accountDto.getPassword()));
user.setEmail(accountDto.getEmail());
user.setRole(new Role(Integer.valueOf(1), user));
repository.save(user);
where accountDto contains the password in clear-text.
Now you can expose a dedicated login method that compares hashed values, something along these lines:
void login(String username, char[] password) throws Exception {
User user = userRepository.findByUsername(username);
if (user != null) {
String encodedPassword = user.getPassword();
if(passwordEncoder.matches(String.valueOf(password), encodedPassword)) {
return;
}
}
throw new Exception("User cannot be authenticated");
}

Related

Problem with Update in CRUD during unit tests because of Bcrypt encryption

I am hashing my user passwords while saving the entities to the database.
While doing that, I'm unable to update the username, without hashing the password again.
I wrote the test for it, which fails.
#Test
#DisplayName("Update")
public void testForUpdate() {
final User user = new User("UserForUpdate", "UpdatedUser123");
this.userService.save(user);
User found = this.userService.findOneByUsernameAndPassword(user.getUsername(), user.getPassword());
assertTrue(found.getId() != null, "Found real user");
this.userService.save(found);
final User asserted = this.userService.findOneByUsernameAndPassword(found.getUsername(), user.getPassword());
assertTrue(asserted != null, "Updated user found");
assertTrue(user.getId() == asserted.getId(), "User ID is persisted");
}
The save method from the UserService looks like this:
#Override
public User save(User newUser) {
newUser.setPassword(passwordEncoder.encode(newUser.getPassword()));
return repository.save(newUser);
}
Am I doing something wrong saving it like this?
How should I proceed, to implement the CRUD Update correctly?
Thanks for any pointers
Do not modify the password when you store the user. Instead encode the password when you modify it.
public class UserSevice {
...
public changePassword(User user, String plainPassword) {
user.setPassword(passwordEncoder.encode(plainPassword));
}
}

How can i get user role from LDAP with spring security

I am able to connect with ldap and getting response, But in my Principal object authorities size is zero in which the role details is available i guess.
What are the additional input i need to pass in order to get ldap role details?
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication()
.userDnPatterns("uid={0},ou=TestOu")
.contextSource()
.url("ldaps://XX:768");
}
i tried with DirContextOperations object also ,it contains many attributes except role, The role is defined in ldapit and i am able to get the role while running the ldap query,
the issue is only through spring security
Please help
A 'role' does not really mean anything for an LDAP Directory Server.
LDAPv3 knows only about static groups.
Some LDAP Directory Server products allows to retrieve group memberships from a 'dynamic attribute' at the entry level.
You may define 'role' as an attribute for entries.
Got it !!!!! implementing a custom AuthenticationProvider and LdapAuthenticator and it used BindAuthenticator. We have to set the following with BindAuthenticator
authenticator.setUserDnPatterns(new String[]{"XX"});
authenticator.setUserAttributes(new String[]{"nsrole"});
In Config
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(this.customLdapAuthenticationProvider());
}
#Bean(name = "ldapAuthenticationProvider")
public AuthenticationProvider customLdapAuthenticationProvider() {
LdapUserDetailsMapper userDetailsMapper = new UserMapper();
CustomLdapAuthenticationProvider provider = new CustomLdapAuthenticationProvider(this.ldapAuthenticator(),
new NullLdapAuthoritiesPopulator());
provider.setUserDetailsContextMapper(userDetailsMapper);
return provider;
}
#Bean(name = "ldapAuthenticator")
public LdapAuthenticator ldapAuthenticator() {
BindAuthenticator authenticator = new BindAuthenticator(this.contextSource());
authenticator.setUserDnPatterns(new String[] { "uid={0},ou=people" });
authenticator.setUserAttributes(new String[] { "nsrole" });
return authenticator;
}
#Bean(name = "contextSource")
public DefaultSpringSecurityContextSource contextSource() {
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(ldapUrl);
return contextSource;
}
private class UserMapper extends LdapUserDetailsMapper {
#Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username,
Collection<? extends GrantedAuthority> authorities) {
List<GrantedAuthority> roles = new ArrayList<GrantedAuthority>();
Attributes attrs = ctx.getAttributes();
Sysout(attr)
UserDetails userDetails = super.mapUserFromContext(ctx, username, roles);
return userDetails;
}
}

Pass object with added properties in Junit Mockito

I'm trying to do the Mockito for a method called generateToken() by using MockitoJUnitRunner.class. The source which I have tried to do as follows.
#RunWith(MockitoJUnitRunner.class)
public class LoginServiceTest {
#Mock
private UserRepository userRepository;
#Mock
private JwtTokenGenerator jwtTokenGenerator;
#InjectMocks
private LoginServiceImpl loginServiceImpl = new LoginServiceImpl();
private JwtUserDto user;
private String jwtSecret;
private String username;
private String password;
/**
* Initialize test data before test cases execution
*/
#Before
public void init() {
user = new JwtUserDto();
user.setId(1L);
user.setUsername("kray1");
user.setRole("Admin");
}
#Test
public void testLogin() {
try {
Mockito.when(jwtTokenGenerator.generateToken(user, jwtSecret)).thenReturn("myToken");
String actual = loginServiceImpl.login(username, password);
assertNotNull(actual);
} catch (Exception e) {
e.printStackTrace();
}
}
For that generateToken() method, I have to pass user object and a string. I'm declaring the user object in Init() method. When I try to execute this, the value return from the login method is null. But when I try to pass the user object as null then it will work as expected. So the problem should be with the user object.
Is there anything, like Mockito is blocking this kind of object with added properties or related thing? Please help to find a way to pass this user object with Mockito.
The LoginServiceImpl class as follows.
public class LoginServiceImpl implements LoginInterface {
#Autowired
private UserRepository userRepository;
#Autowired
private JwtTokenGenerator jwtTokenGenerator;
/*
* (non-Javadoc)
*/
public String login(String userName, String password) {
if (userName != null && password != null && !userName.isEmpty() && !password.isEmpty()) {
List<UserAuthenticationInfo> authInfo = userRepository.findUserRolesByUsernamePassword(userName, password);
if (authInfo != null && !authInfo.isEmpty()) {
JwtUserDto user = new JwtUserDto();
user.setId((long) authInfo.get(0).getUserId());
user.setUsername(userName);
user.setRole(authInfo.get(0).getUserRole());
return jwtTokenGenerator.generateToken(user, jwtSecret);
}
}
return null;
}
}
Do you have equals/hashcode on User class?
What is the result if you setup mock using
Mockito.when(jwtTokenGenerator.generateToken(any(User.class),any(String.class))
.thenReturn("myToken");
explanation:
When setting expectation as
Mockito.when(jwtTokenGenerator.generateToken(user, jwtSecret)).then...
You instruct your mock to act only for given user object. equals method is used for that. So, if your User is missing equals method, then reference equality is used. Two User objects (each crated with separate new User() call will not be equal.
For non-matching parameters in Mockito.when your mock (thenReturn) is not applied. Default value (null) is returned from mock.
Therefore I prefer to setup mocks not for specific arguments and then use Mockito.verify to check if expected interactions with mock took place. That way your tests are more expressive. Actually most of my object have equals/hashode not because of business reasons (I do not put them in collections) but only for testing and comparing using assertEquals.
Side note:
do not catch (Exception e) { e.printStackTrace(); } in test. It is much easier just to declare test method to throw Exception. End result is same (stacktrace printed) but with less code.
You are probably creating a new JwtUserDto() in your production code or getting the user instance from another mock. If you haven't overwritten the equals() method in your JwtUserDto class your 'test' user won't equal the 'production' user.
Make sure that the production and test user are the same instance or that they .equals each other.

Gradle not recognizing mysql dependency

I am trying to move from H2 in memory database to mysql and I incorporated compile("mysql:mysql-connector-java:5.1.6") to my build.gradle after removing the H2 dependency. I also put the following in my application.properties:
spring.datasource.url=jdbc:mysql://localhost:3306/mutibodb
spring.datasource.username=xxx
spring.datasource.password=xxx
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.hibernate.ddl-auto: create
However, when I rebuild, I get this error: http://pastebin.com/4cS9Dk0U
This is the same error I get if I don't put either mysql or h2 dependency, which means it does not take it at all.
My full existing code which was working with H2 database is here: https://github.com/devdeep1987/MutiboProject/tree/master/MutiboServer
Could you please tell me if there is a step I missed.
Update:
My CustomUserDetailsService class is the following:
#Service
public class CustomUserDetailsService implements UserDetailsService {
private UserRepository repository;
private static final Logger logger = LoggerFactory.getLogger(CustomUserDetailsService.class);
#Autowired
public CustomUserDetailsService(UserRepository repo) {
this.repository = repo;
}
#Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
logger.info("username:"+name);
User u = repository.findByUsername(name);
if (u == null)
throw new UsernameNotFoundException("User details not found with this username: " + name);
String username = u.getUsername();
String password = u.getPassword();
List authList = new ArrayList();
authList.add(new SimpleGrantedAuthority("USER"));
//get the encoded password
//String encodedPassword = passwordEncoder.encode(password);
org.springframework.security.core.userdetails.User user = new org.springframework.security.core.userdetails.User(username, password, authList);
return user;
}
public boolean createUser(String username, String password) {
User existing = repository.findByUsername(username);
if(existing!=null)
return false;
User u = new User();
u.setUsername(username);
u.setPassword(password);
repository.save(u);
return true;
}
private List getAuthorities(String role) {
List authList = new ArrayList();
authList.add(new SimpleGrantedAuthority("USER"));
//you can also add different roles here
//for example, the user is also an admin of the site, then you can add ROLE_ADMIN
//so that he can view pages that are ROLE_ADMIN specific
if (role != null && role.trim().length() > 0) {
if (role.equals("admin")) {
authList.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
}
}
return authList;
}
}
and my UserRepository class is:
#Repository
public interface UserRepository extends CrudRepository<User, Long>{
User findByUsername(String username);
}
Changing the version of spring-boot-gradle-plugin from 1.0.2.RELEASE to 1.1.4.RELEASE fixes this. Not sure why though.

How to use GWT with Apache Shiro hashed and salted [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
In this small tutorial I show you how to build a GWT Module which is responsible for registration and login.
The password gets hashed with Sha256 and salted.
Download and Installation
Download for Apache Shiro: http://shiro.apache.org/download.html ;
I have used Shrio-All (1.2.2 Binary Distribution) http://tweedo.com/mirror/apache/shiro/1.2.2/shiro-root-1.2.2-source-release.zip
After the download include shiro-all-1.2.2.jar in your lib folder.
We can also include other .jar files which we will need later on.
MySQL Driver: http://www.java2s.com/Code/Jar/c/Downloadcommysqljdbc515jar.htm (com.mysql.jdbc_5.1.5.jar)
SLF4J Logging: http://www.slf4j.org/download.html (slf4j-api-1.7.5.jar, slf4j-simple-1.7.5.jar)
Apache Commons Beanutils: http://repo2.maven.org/maven2/commons-beanutils/commons-beanutils/1.7.0/ (commons-beanutils-1.7.0.jar)
Don't forget to add your jars to your build path.
web.xml
Add this to your web.xml
<!-- Apache Shero -->
<listener>
<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>
<filter>
<filter-name>ShiroFilter</filter-name>
<filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>
<!-- Make sure any request you want accessible to Shiro is filtered. /* catches all -->
<!-- requests. Usually this filter mapping is defined first (before all others) to -->
<!-- ensure that Shiro works in subsequent filters in the filter chain: -->
<filter-mapping>
<filter-name>ShiroFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
shiro.ini
Put your shiro.ini into WEB-INF:
[main]
authc.loginUrl = /Login.html?gwt.codesvr=127.0.0.1:9997
authc.successUrl = /Leitfaden.html
logout.redirectUrl = /login.html
# ------------------------
# Database
# Own Realm
jdbcRealm = leitfaden.login.server.MyRealm
# Sha256
sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
# base64 encoding, not hex in this example:
sha256Matcher.storedCredentialsHexEncoded = false
sha256Matcher.hashIterations = 1024
jdbcRealm.credentialsMatcher = $sha256Matcher
# User Query
# default is "select password from users where username = ?"
jdbcRealm.authenticationQuery = SELECT password, salt FROM USER WHERE email = ?
# Connection
ds = com.mysql.jdbc.jdbc2.optional.MysqlDataSource
ds.serverName = localhost
ds.user = root
ds.password = root
ds.databaseName = leitfaden
jdbcRealm.dataSource=$ds
authc.usernameParam = email
authc.passwordParam = password
authc.failureKeyAttribute = shiroLoginFailure
# Use Built-in Chache Manager
builtInCacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager
securityManager.cacheManager = $builtInCacheManager
# -----------------------------------------------------------------------------
[urls]
/yourMainUrl.html = authc
GWT Module
Create a module for login. Module name “Login” and Package name “leitfaden.login”:
Add this to your web.xml
<servlet>
<servlet-name>LoginService</servlet-name>
<servlet-class>leitfaden.login.server.LoginServiceImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginService</servlet-name>
<url-pattern>/leitfaden.login.Login/LoginService</url-pattern>
</servlet-mapping>
LoginService.java
#RemoteServiceRelativePath("LoginService")
public interface LoginService extends RemoteService {
public Boolean isLoggedIn();
public Boolean tryLogin(String email, String password, Boolean rememberMe);
public void logout();
public void registrate(String email, String password);
}
LoginServiceAsync.java
public interface LoginServiceAsync {
public void isLoggedIn(AsyncCallback<Boolean> callback);
public void tryLogin(String email, String password, Boolean rememberMe, AsyncCallback<Boolean> callback);
public void logout(AsyncCallback<Void> callback);
public void registrate(String email, String password, AsyncCallback<Void> callback);
}
LoginServiceImpl
public class LoginServiceImpl extends RemoteServiceServlet implements LoginService {
private static final long serialVersionUID = -4051026136441981243L;
private static final transient Logger log = LoggerFactory
.getLogger(LoginServiceImpl.class);
private org.apache.shiro.subject.Subject currentUser;
public LoginServiceImpl() {
Factory<SecurityManager> factory = new IniSecurityManagerFactory();
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
}
#Override
public Boolean isLoggedIn() {
currentUser = SecurityUtils.getSubject();
if (currentUser.isAuthenticated()) {
return true;
} else {
return false;
}
}
#Override
public Boolean tryLogin(String username, String password, Boolean rememberMe) {
// get the currently executing user:
currentUser = SecurityUtils.getSubject();
// let's login the current user so we can check against roles and
// permissions:
if (!currentUser.isAuthenticated()) {
//collect user principals and credentials in a gui specific manner
//such as username/password html form, X509 certificate, OpenID, etc.
//We'll use the username/password example here since it is the most common.
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
//this is all you have to do to support 'remember me' (no config - built in!):
token.setRememberMe(rememberMe);
try {
currentUser.login(token);
log.info("User [" + currentUser.getPrincipal().toString() + "] logged in successfully.");
return true;
} catch (UnknownAccountException uae) {
log.info("There is no user with username of "
+ token.getPrincipal());
} catch (IncorrectCredentialsException ice) {
log.info("Password for account " + token.getPrincipal()
+ " was incorrect!");
} catch (LockedAccountException lae) {
log.info("The account for username " + token.getPrincipal()
+ " is locked. "
+ "Please contact your administrator to unlock it.");
} catch (AuthenticationException ae) {
log.error(ae.getLocalizedMessage());
}
}
return false;
}
#Override
public void logout() {
currentUser = SecurityUtils.getSubject();
currentUser.logout();
}
#Override
public void registrate(String email, String plainTextPassword) {
RandomNumberGenerator rng = new SecureRandomNumberGenerator();
Object salt = rng.nextBytes();
// Now hash the plain-text password with the random salt and multiple
// iterations and then Base64-encode the value (requires less space than Hex):
String hashedPasswordBase64 = new Sha256Hash(plainTextPassword, salt,1024).toBase64();
User user = new User(email, hashedPasswordBase64, salt.toString(), 0);
this.createUser(user);
}
private void createUser(User user) {
UserDAL.connect();
UserDAL.beginTransaction();
new UserDAL().createUser(user);
log.info("User with email:" + user.getEmail() + " hashedPassword:"+ user.getPassword() + " salt:" + user.getSalt());
UserDAL.commitTransaction();
UserDAL.disconnect();
}
}
MyRealm.java
Users can now register at this application. But Shiro does not know how to compare salted passwords with the given user input. For that we need to implement our own Realm. A Realm is essentially a security-specific DAO.
MyRealm.java gets the user with the given email and returns a SaltedAuthenticationInfo. With that SaltedAuthenticationInfo Shiro knows how to compare the user input with the user from the database.
public class MyRealm extends JdbcRealm {
private static final Logger log = LoggerFactory.getLogger(MyRealm.class);
#Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// identify account to log to
UsernamePasswordToken userPassToken = (UsernamePasswordToken) token;
final String username = userPassToken.getUsername();
if (username == null) {
log.debug("Username is null.");
return null;
}
// read password hash and salt from db
final PasswdSalt passwdSalt = getPasswordForUser(username);
if (passwdSalt == null) {
log.debug("No account found for user [" + username + "]");
return null;
}
// return salted credentials
SaltedAuthenticationInfo info = new MySaltedAuthentificationInfo(username, passwdSalt.password, passwdSalt.salt);
return info;
}
private PasswdSalt getPasswordForUser(String username) {
User user = getUserByEmail(username);
if (user == null) {
return null;
}
return new PasswdSalt(user.getPassword(), user.getSalt());
}
private User getUserByEmail(String email) {
UserDAL.connect();
User user = new UserDAL().getUserByEmail(email);
UserDAL.disconnect();
return user;
}
class PasswdSalt {
public String password;
public String salt;
public PasswdSalt(String password, String salt) {
super();
this.password = password;
this.salt = salt;
}
}
}
MySaltedAuthentificationInfo
Important is that you decode the salt correctly in getCredentialsSalt().I have used Base64.
public class MySaltedAuthentificationInfo implements SaltedAuthenticationInfo {
private static final long serialVersionUID = -2342452442602696063L;
private String username;
private String password;
private String salt;
public MySaltedAuthentificationInfo(String username, String password, String salt) {
this.username = username;
this.password = password;
this.salt = salt;
}
#Override
public PrincipalCollection getPrincipals() {
PrincipalCollection coll = new SimplePrincipalCollection(username, username);
return coll;
}
#Override
public Object getCredentials() {
return password;
}
#Override
public ByteSource getCredentialsSalt() {
return new SimpleByteSource(Base64.decode(salt));
}
}
Users can now register and login. You would only need to code views in your login module that call the LoginService.