Gradle not recognizing mysql dependency - mysql

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.

Related

Spring Batch read DB2 write Mysql duplicated records in Resultset

I'm trying to read from DB2 with a stored procedure ( SP ) that returns records based on page size and start page among other input parameters. My goal is however to request all the records in one call and load another table in Mysql using Spring Batch. Seems straight forward enough, however when I run the job below it returns the correct number of records as per the SP but the records are skewed and duplicated as if the transactions are not working properly. The reader uses one #Primary Datasources ( DS ), for the DB2 read and another DS for the JpaItemWriter destination (LocalContainerEntityManagerFactoryBean).
My understanding is that DB2 and Mysql returns a ResultSet so don't know why I am forced to add these lines to avoid Invalid Parameter Type or Cursor position errors....
reader.setVerifyCursorPosition(false);
reader.setRefCursorPosition(0);
I am reading from DB2 then inserting into a MySql table based on the supplied AccountCashRowMapper. It's a strange situation that almost works, the listener doesn't report any errors.
Spring Batch is supposed to inherently manage the transactions for me, however what configuration or code is missing to make this work ?
#EnableBatchProcessing
public class LoadAccountCashTableJob {
private static final Logger logger = LoggerFactory.getLogger(LoadAccountCashTableJob.class);
#Autowired
ApplicationContext context;
#Autowired
public AccountCashRepository repo;
#Autowired
public EntityManager em;
#Autowired
private JobBuilderFactory jobBuilderFactory;
#Autowired
private StepBuilderFactory stepBuilderFactory;
#Autowired
private AccountCashProcessor accountCashProcessor;
#Autowired
private TaskExecutor taskExecutor;
#Autowired
#Qualifier("originDataSource")
protected DataSource dataSource;
#Qualifier(value = "AccountCashJob")
#Bean
public Job AccountCashJob() throws Exception{
return this.jobBuilderFactory.get("AccountCashJob")
.start(this.accountCashStep())
.build();
}
#Bean
public Step accountCashStep() throws Exception{
return this.stepBuilderFactory.get("accountCashStep")
//.transactionManager(transactionManager)
.<String, String>chunk(500)
.reader(this.cashSPReader(dataSource))
.processor(accountCashProcessor)
.writer(this.accountCashItemWriter() )
.taskExecutor(taskExecutor)
.listener(new ItemFailureLoggerListener())
.build();
}
#Bean(destroyMethod="")
#StepScope
public StoredProcedureItemReader cashSPReader (DataSource dataSource) throws Exception {
StoredProcedureItemReader reader = new StoredProcedureItemReader();
SqlParameter[] parameters = new SqlParameter[7];
parameters[0] = new SqlParameter("DIV", Types.VARCHAR);
parameters[1] = new SqlParameter("PageDebut", Types.SMALLINT);
parameters[2] = new SqlParameter("NbParPage",Types.SMALLINT);
parameters[3] = new SqlInOutParameter("O_NB_PAGES",Types.SMALLINT);
parameters[4] = new SqlInOutParameter("O_RC",Types.INTEGER);
parameters[5] = new SqlInOutParameter("O_RC_DSC",Types.VARCHAR);
parameters[6] = new SqlReturnResultSet(ACCOUNTCASH_RESULT_SET, new AccountCashRowMapper());
reader.setDataSource(dataSource);
reader.setProcedureName("VMDTSTSP.db2cdb_List_Positions_Encaisse");
reader.setRowMapper(new AccountCashRowMapper()); //FOR the output records..
reader.setParameters(parameters);
reader.setPreparedStatementSetter(new SPParamSetter());
reader.afterPropertiesSet();
logger.info("reader.getSql()>>>>>>>>>>>>>>"+reader.getSql());
reader.setVerifyCursorPosition(false);
reader.setRefCursorPosition(0);
return reader;
}
#Bean(destroyMethod="")
#StepScope
public JpaItemWriter<AccountCash> accountCashItemWriter() {
EntityManagerFactory emf = (EntityManagerFactory) context.getBean("entityManagerFactory");
em.createNativeQuery("TRUNCATE TABLE purefacts.account_cash").executeUpdate();
JpaItemWriter<AccountCash> accountCashJpaItemWriter = new JpaItemWriter<>();
accountCashJpaItemWriter.setEntityManagerFactory(emf);
accountCashJpaItemWriter.setUsePersist(true);
return accountCashJpaItemWriter;
}
public static class SPParamSetter implements PreparedStatementSetter {
#Override
public void setValues(PreparedStatement ps) throws SQLException {
AS400JDBCCallableStatement eventCallableSt=(AS400JDBCCallableStatement)ps;
eventCallableSt.setString(1, ACCOUNTCASH_DIV);
eventCallableSt.setInt(2, ACCOUNTCASH_START_PAGE);
eventCallableSt.setInt(3,ACCOUNTCASH_PAGE_SIZE);
eventCallableSt.registerOutParameter(4, Types.SMALLINT);
eventCallableSt.setInt(4, ACCOUNTCASH_O_NB_PAGES);
eventCallableSt.registerOutParameter(5, Types.INTEGER);
eventCallableSt.setInt(5, ACCOUNTCASH_O_ERROR_CODE);
eventCallableSt.registerOutParameter(6, Types.VARCHAR);
eventCallableSt.setString(6, ACCOUNTCASH_O_ERROR_DESCRIPTION);
eventCallableSt.getResultSet();
}
}
public class ItemFailureLoggerListener extends ItemListenerSupport {
private Log logger = LogFactory.getLog("item.error");
public void onReadError(Exception e) {
logger.error("Encountered error on read", e);
}
public void onWriteError(Exception ex, List items) {
logger.error("Encountered error on write", ex);
}
}
}

Match password efficiently in Spring Boot - JPA

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");
}

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;
}
}

How to connect to multiple MySQL databases as per the header in REST API request

I'm creating a multi tenant spring boot - JPA application.
In this application, I want to connect to MySQL Databases using DB name which is sent through API request as header.
I checked many multi tenant project samples online but still can't figure out a solution.
Can anyone suggest me a way to do this?
You can use AbstractRoutingDataSource to achieve this. AbstractRoutingDataSource requires information to know which actual DataSource to route to(referred to as Context), which is provided by determineCurrentLookupKey() method. Using example from here.
Define Context like:
public enum ClientDatabase {
CLIENT_A, CLIENT_B
}
Then you need to define Context Holder which will be used in determineCurrentLookupKey()
public class ClientDatabaseContextHolder {
private static ThreadLocal<ClientDatabase> CONTEXT = new ThreadLocal<>();
public static void set(ClientDatabase clientDatabase) {
Assert.notNull(clientDatabase, "clientDatabase cannot be null");
CONTEXT.set(clientDatabase);
}
public static ClientDatabase getClientDatabase() {
return CONTEXT.get();
}
public static void clear() {
CONTEXT.remove();
}
}
Then you can extend AbstractRoutingDataSource like below:
public class ClientDataSourceRouter extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return ClientDatabaseContextHolder.getClientDatabase();
}
}
Finally, DataSource bean configuration:
#Bean
public DataSource clientDatasource() {
Map<Object, Object> targetDataSources = new HashMap<>();
DataSource clientADatasource = clientADatasource();
DataSource clientBDatasource = clientBDatasource();
targetDataSources.put(ClientDatabase.CLIENT_A,
clientADatasource);
targetDataSources.put(ClientDatabase.CLIENT_B,
clientBDatasource);
ClientDataSourceRouter clientRoutingDatasource
= new ClientDataSourceRouter();
clientRoutingDatasource.setTargetDataSources(targetDataSources);
clientRoutingDatasource.setDefaultTargetDataSource(clientADatasource);
return clientRoutingDatasource;
}
https://github.com/wmeints/spring-multi-tenant-demo
Following this logic, I can solve it now. Some of the versions need to be upgraded and the codes as well.
Spring Boot version have changed.
org.springframework.boot
spring-boot-starter-parent
2.1.0.RELEASE
Mysql version has been removed.
And some small changed in MultitenantConfiguration.java
#Configuration
public class MultitenantConfiguration {
#Autowired
private DataSourceProperties properties;
/**
* Defines the data source for the application
* #return
*/
#Bean
#ConfigurationProperties(
prefix = "spring.datasource"
)
public DataSource dataSource() {
File[] files = Paths.get("tenants").toFile().listFiles();
Map<Object,Object> resolvedDataSources = new HashMap<>();
if(files != null) {
for (File propertyFile : files) {
Properties tenantProperties = new Properties();
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create(this.getClass().getClassLoader());
try {
tenantProperties.load(new FileInputStream(propertyFile));
String tenantId = tenantProperties.getProperty("name");
dataSourceBuilder.driverClassName(properties.getDriverClassName())
.url(tenantProperties.getProperty("datasource.url"))
.username(tenantProperties.getProperty("datasource.username"))
.password(tenantProperties.getProperty("datasource.password"));
if (properties.getType() != null) {
dataSourceBuilder.type(properties.getType());
}
resolvedDataSources.put(tenantId, dataSourceBuilder.build());
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
// Create the final multi-tenant source.
// It needs a default database to connect to.
// Make sure that the default database is actually an empty tenant database.
// Don't use that for a regular tenant if you want things to be safe!
MultitenantDataSource dataSource = new MultitenantDataSource();
dataSource.setDefaultTargetDataSource(defaultDataSource());
dataSource.setTargetDataSources(resolvedDataSources);
// Call this to finalize the initialization of the data source.
dataSource.afterPropertiesSet();
return dataSource;
}
/**
* Creates the default data source for the application
* #return
*/
private DataSource defaultDataSource() {
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create(this.getClass().getClassLoader())
.driverClassName(properties.getDriverClassName())
.url(properties.getUrl())
.username(properties.getUsername())
.password(properties.getPassword());
if(properties.getType() != null) {
dataSourceBuilder.type(properties.getType());
}
return dataSourceBuilder.build();
}
}
This change is here due to the DataSourceBuilder has been moved to another path and its constructor has been changed.
Also changed the MySQL driver class name in application.properties like this
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

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.