I am trying to build an simple CRM spring app with a security layer.
My use case is simple : login page which allows the access the customer list and also to add new user from it.
I have created a client config class for the customer management and a security config class.
The security config file defined it own data source, transactional manager and session factory to access a dedicated db which manage the users :
#Configuration
#EnableWebSecurity
#PropertySource("classpath:security-persistence-mysql.properties")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
Environment env;
private Logger logger = Logger.getLogger(getClass().getName());
#Autowired
private UserDetailsService userService;
#Autowired
private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;
#Bean(name = "securitySessionFactory")
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(securityDataSource());
sessionFactory.setHibernateProperties(hibernateProperties());
sessionFactory.setPackagesToScan("com.luv2code.springdemo");
return sessionFactory;
}
#Bean(name = "securityDataSource")
public DataSource securityDataSource() {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
try {
dataSource.setDriverClass(env.getProperty("jdbc.driver"));
} catch (PropertyVetoException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
dataSource.setUser(env.getProperty("jdbc.user"));
dataSource.setPassword(env.getProperty("jdbc.password"));
dataSource.setJdbcUrl(env.getProperty("jdbc.security.url"));
logger.info("URL security config : " + env.getProperty("jdbc.url"));
dataSource.setInitialPoolSize(
getPropertyAsInt("connection.pool.initialPoolSize"));
dataSource.setMinPoolSize(
getPropertyAsInt("connection.pool.minPoolSize"));
dataSource.setMaxPoolSize(
getPropertyAsInt("connection.pool.maxPoolSize"));
dataSource.setMaxIdleTime(
getPropertyAsInt("connection.pool.maxIdleTime"));
return dataSource;
}
private int getPropertyAsInt(String key) {
return Integer.parseInt(env.getProperty(key));
}
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(authenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/customer/**").hasRole("EMPLOYE")
.antMatchers("/leaders/**").hasRole("MANAGER")
.antMatchers("/systems/**").hasRole("ADMIN")
.antMatchers("/", "/home", "/createUser").permitAll()
.anyRequest().authenticated().and().formLogin()
.loginPage("/showMyLoginPage")
.loginProcessingUrl("/authentificateTheUser")
.successHandler(customAuthenticationSuccessHandler).permitAll()
.and().logout().permitAll().and().exceptionHandling()
.accessDeniedPage("/access-denied");
}
private Properties hibernateProperties() {
Properties hibernatePpt = new Properties();
hibernatePpt.setProperty("hibernate.dialect",
env.getProperty("hibernate.dialect"));
hibernatePpt.setProperty("hibernate.show_sql",
env.getProperty("hibernate.show_sql"));
hibernatePpt.setProperty("hibernate.packagesToScan",
env.getProperty("hibernate.packagesToScan"));
return hibernatePpt;
}
#Bean(name = "securtiyTransactionManager")
#Autowired
#Qualifier("securitySessionFactory")
public HibernateTransactionManager transactionManager(
SessionFactory sessionFactory) {
// setup transaction manager based on session factory
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory);
return txManager;
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
auth.setUserDetailsService(userService);
auth.setPasswordEncoder(passwordEncoder());
return auth;
}
}
For the security and login I am not using the default user management but a custom one with a Role and User entity with a join table :
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "username")
private String userName;
#Column(name = "password")
private String password;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "email")
private String email;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "users_roles", joinColumns = #JoinColumn(name = "user_id"), inverseJoinColumns = #JoinColumn(name = "role_id"))
private Collection<Role> roles;
//constructors, getter,setter
}
Role :
#Entity
#Table(name = "role")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
//.....
}
I use a service and dao layer to access the user with hibernate.
My issue is that when I try to loggin or add a user I have an exception : Caused by: javax.persistence.TransactionRequiredException: no transaction is in progress
The thing is use the transactional annotation.
First I go through the controller :
#Controller
public class LoginController {
#GetMapping("/showMyLoginPage")
public String showMyLoginPage() {
return "login";
}
#GetMapping("/access-denied")
public String showAccesDenied() {
return "access-denied";
}
}
Then the jsp :
<form:form method="POST" action="${pageContext.request.contextPath}/authenticateTheUser">
User name :
Password :
</form:form>
<form:form method="GET" action="${pageContext.request.contextPath}/createUser">
<input type="submit" value="Registration">
</form:form>
When the jsp is submitted it calls the user service details (Autowired in the config class) and the method loadUserByUsername which is under transaction :
public interface UserService extends UserDetailsService
#Service
#Transactional("securtiyTransactionManager")
public class UserServiceImpl implements UserService {
#Autowired
private UserDAO userDAO;
#Override
public UserDetails loadUserByUsername(String userName)
throws UsernameNotFoundException {
User user = userDAO.getUser(userName);
if (user == null) {
throw new UsernameNotFoundException(
"Invalid username or password.");
}
return new org.springframework.security.core.userdetails.User(
user.getUserName(), user.getPassword(),
mapRolesToAuthorities(user.getRoles()));
}
private Collection<? extends GrantedAuthority> mapRolesToAuthorities(
Collection<Role> roles) {
return roles.stream()
.map(role -> new SimpleGrantedAuthority(role.getName()))
.collect(Collectors.toList());
}
}
Somehow the transaction is not working but I don't know why.
I parallel I have my other config file which manage the customer use cases.
Thx for any help to understand what is wrong.
For information the full project is here : https://github.com/moutyque/CRM
#Bean(name = "securtiyTransactionManager")
#Autowired
public HibernateTransactionManager transactionManager(
#Qualifier("securitySessionFactory") SessionFactory sessionFactory) {
// setup transaction manager based on session factory
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory);
return txManager;
}
I can only guess your error with just looking at it. You can open log level for transactions to examine issue. Qualifier can be inline in method parameter to ensure securitySessionFactory is autowired.
Related
I'm building a multitenant application and used this as a source of reference: https://www.baeldung.com/multitenancy-with-spring-data-jpa. Although most of the DB calls work fine, some are exhibiting confusing behavior. Here is an example:
The following class MultiTenantConfigurationprovides all the necessary beans for configuring the datasource, sessions and transactions:
class MultiTenantConfiguration {
#Bean
public DataSource dataSource() {
AbstractRoutingDataSource dataSource = new RoutingDataSource();
dataSource.setTargetDataSources(....);
dataSource.setDefaultTargetDataSource(....);
dataSource.afterPropertiesSet();
return dataSource;
}
#Bean
public SessionFactory entityManagerFactory(DataSource dataSource) {
LocalSessionFactoryBuilder builder = new LocalSessionFactoryBuilder(dataSource);
builder.scanPackages("....");
return builder.buildSessionFactory();
}
#Bean
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
return new HibernateTransactionManager(sessionFactory);
}
}
The following class provides the id of DB source to lookup:
class RoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return TENANT_CONTEXT.getCurrentTenant();
}
}
This is the API service which calls the internal user service:
class UserAPIResource {
#GET
#Path("/api/users_0/{id}")
#Produces(MediaType.APPLICATION_JSON)
public User getUser0(#PathParam("id") Long id) {
return userService.getUserForId_0(id);
}
#GET
#Path("/api/users_1/{id}")
#Produces(MediaType.APPLICATION_JSON)
public User getUser1(#PathParam("id") Long id) {
return userService.getUserForId_1(id);
}
#GET
#Path("/api/users_2/{id}")
#Produces(MediaType.APPLICATION_JSON)
public User getUser2(#PathParam("id") Long id) {
return userService.getUserForId_2(id);
}
}
This is the UserService that communicates with the JPA repo - UserRepository, converts it into a consumable POJO and returns:
class UserService {
#Autowired
UserRepository userRepo;
public User getUserForId_0(Long id) {
RawUser rawUser= userRepo.getById(id);
User user = new User();
user.setId(rawUser.getId());
user.setName(rawUser.getName());
return user;
}
#Transactional
public User getUserForId_1(Long id) {
RawUser rawUser= userRepo.getById(id);
User user = new User();
user.setId(rawUser.getId());
user.setName(rawUser.getName());
return user;
}
public User getUserForId_2(Long id) {
RawUser rawUser= userRepo.findById(id);
User user = new User();
user.setId(rawUser.getId());
user.setName(rawUser.getName());
return user;
}
}
Here is the RawUser entity class. It does not have any mappings to other entities:
#Entity
#EntityListeners(AuditingEntityListener.class)
class RawUser {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
#Column(name = "name")
private String name;
public long getId() {return id;}
public String getName() {return name;}
}
Now, /api/users_0/1 throws: org.hibernate.LazyInitializationException: could not initialize proxy [com.db.model.User#1] - no Session.
However, /api/users_1/1 and /api/users_2/1 return successfully.
The difference is that /api/users_1/1 which calls userService.getUserForId_1() is marked as Transactional.
And /api/users_2/1 which calls userService.getUserForId_1() uses findById() instead of getById().
This behavior only happens in the multi-tenant structure. So I'm guessing I'm missing some additional configuration, specifically in Sessions/Proxy? If someone can explain this, that'd be great.
I'm not using spring-boot in this app.
I'm testing profiles to use different datasource in integration tests.
I have entity User as follow:
#Table(name = "user_inf")
#Entity
#NamedQuery(name="User.findById", query="select u from User u where u.id=:id")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
#Column(name = "userName", length = 25)
private String userName;
#Column(name = "userEmail", unique = true, length = 320)
private String userEmail;
}
for that entity I have the service and dao(Service only invokes dao method)
UserDao :
#Repository
#Transactional
public class UserDaoImpl implements UserDao {
#PersistenceContext
private EntityManager entityManager;
#Override
public User findById(Long id) {
TypedQuery<User> query = entityManager.createNamedQuery("User.findById", User.class);
query.setParameter("id", id);
return query.getSingleResult();
}
}
User service :
#Service
public class UserServiceImpl implements UserService{
#Autowired
private UserDao userDao;
#Override
public User getUser(Long id) {
return userDao.findById(id);
}
}
#Configuration
#PropertySource(value = {"classpath:database/jdbc.properties"})
#EnableTransactionManagement
#ComponentScan({"com.example.test.repository", "com.example.test.service"})
public class SpringConfig {
private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
private static final String PROPERTY_NAME_HIBERNATE_MAX_FETCH_DEPTH = "hibernate.max_fetch_depth";
private static final String PROPERTY_NAME_HIBERNATE_JDBC_FETCH_SIZE = "hibernate.jdbc.fetch_size";
private static final String PROPERTY_NAME_HIBERNATE_JDBC_BATCH_SIZE = "hibernate.jdbc.batch_size";
private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
private static final String ENTITY_MANAGER_PACKAGES_TO_SCAN = "com.example.test.entity";
#Autowired
private Environment env;
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
dataSource.setUrl(env.getProperty("jdbc.url"));
dataSource.setUsername(env.getProperty("jdbc.username"));
dataSource.setPassword(env.getProperty("jdbc.password"));
return dataSource;
}
#Bean
#Profile("test")
public DataSource dataSourceForTest() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
dataSource.setUrl(env.getProperty("jdbc.test.url"));
dataSource.setUsername(env.getProperty("jdbc.username"));
dataSource.setPassword(env.getProperty("jdbc.password"));
return dataSource;
}
#Bean
public PlatformTransactionManager jpaTransactionManager(LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactoryBean.getObject());
return transactionManager;
}
private HibernateJpaVendorAdapter vendorAdaptor() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setShowSql(true);
return vendorAdapter;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(DataSource mainDataSource) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setJpaVendorAdapter(vendorAdaptor());
entityManagerFactoryBean.setDataSource(mainDataSource);
entityManagerFactoryBean.setPackagesToScan(ENTITY_MANAGER_PACKAGES_TO_SCAN);
entityManagerFactoryBean.setJpaProperties(jpaHibernateProperties());
return entityManagerFactoryBean;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
private Properties jpaHibernateProperties() {
Properties properties = new Properties();
properties.put(PROPERTY_NAME_HIBERNATE_MAX_FETCH_DEPTH, env.getProperty(PROPERTY_NAME_HIBERNATE_MAX_FETCH_DEPTH));
properties.put(PROPERTY_NAME_HIBERNATE_JDBC_FETCH_SIZE, env.getProperty(PROPERTY_NAME_HIBERNATE_JDBC_FETCH_SIZE));
properties.put(PROPERTY_NAME_HIBERNATE_JDBC_BATCH_SIZE, env.getProperty(PROPERTY_NAME_HIBERNATE_JDBC_BATCH_SIZE));
properties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, env.getProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
properties.put(PROPERTY_NAME_HIBERNATE_DIALECT, env.getProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
properties.put("hibernate.hbm2ddl.auto", "none");
return properties;
}
}
Property file which is used for datasource and hibernate (jdbc.properties) contains following :
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/testdatabase
jdbc.username=Bruce
jdbc.password=givanchy
jdbc.test.url=jdbc:mysql://localhost:3306/testdatabase1
hibernate.max_fetch_depth = 3
hibernate.jdbc.fetch_size = 50
hibernate.jdbc.batch_size = 10
hibernate.show_sql = true
hibernate.dialect = org.hibernate.dialect.MySQL8Dialect
Entry point for application :
public class EntryClass {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = applicationContext.getBean("userServiceImpl", UserServiceImpl.class);
User user =userService.getUser(1L);
System.out.println("userName is "+user.getUserName());
}
}
It works as it should.
But in test source
I have only one test for service to understand how profiles work
#SpringJUnitConfig(SpringConfig.class)
#ActiveProfiles("test")
class UserServiceImplTest {
#Autowired
private UserService userService;
#Test
void getUser() {
User user = userService.getUser(5L);
Assertions.assertEquals("Vector", user.getUserName());
}
}
And I get "No qualifying bean" exception because there are two beans of datasource type, but I set which profile it should use?Can you explain why It doesn't work?
When you launch it normally, the datasource bean with 'test' profile is not created. (becasue there is no test profile set.)
When you run it as a test, then both datasource beans are created. The default is created because there is no any condition on it, and the other is becasue its annotated with the test profile.
Simply add #Profile("!test") to the default bean. This way it will be created only if the test profile is NOT active.
I am learning Spring MVC and trying to implement spring security in my application. I have created custom login and trying to login using custom login form. While I am running application Login page is appearing properly but after entering the username and password it's not working. Submitting login form is calling the same login page, but with error in URL.
My UserDetailsService:
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserService userService;
#Transactional
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User user = userService.getUserByEmail(email);
if (user != null) {
List<GrantedAuthority> authorities = getUserAuthority(user.getRoles());
return buildUserForAuthentication(user, authorities);
} else {
throw new UsernameNotFoundException("user with email " + email + " does not exist.");
}
}
private List<GrantedAuthority> getUserAuthority(Set<Role> userRoles) {
Set<GrantedAuthority> roles = new HashSet<>();
userRoles.forEach((role) -> {
roles.add(new SimpleGrantedAuthority(role.getName()));
});
return new ArrayList<GrantedAuthority>(roles);
}
private UserDetails buildUserForAuthentication(User user, List<GrantedAuthority> authorities) {
return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), authorities);
}
}
WebSecurityConfig
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Qualifier("customUserDetailsService")
#Autowired
private UserDetailsService userDetailsService;
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/profile").authenticated()
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/", true)
.permitAll()
.and()
.httpBasic()
.and()
.csrf().disable()
.logout()
.logoutSuccessUrl("/");
}
}
User class:
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String email;
private String password;
private String firstName;
private String lastName;
private String joinedDate;
#Embedded
#AttributeOverrides({
#AttributeOverride( name = "country", column = #Column(name = "country")),
#AttributeOverride( name = "city", column = #Column(name = "city")),
#AttributeOverride( name = "zipCode", column = #Column(name = "zip_code")),
#AttributeOverride( name = "street", column = #Column(name = "street")),
#AttributeOverride( name = "homeNumber", column = #Column(name = "home_number"))
})
private Address address;
private String phoneNumber;
#ManyToMany
private Set<Role> roles = new HashSet<Role>();
GETTERS AND SETTERS
UserService:
#Service
#Transactional
public class UserServiceImpl implements UserService {
#Autowired
private UserRepository userRepository;
#Autowired
private PasswordEncoder bCryptPasswordEncoder;
#Autowired
private RoleRepository roleRepository;
#Override
public User getUserByEmail(String email) {
return userRepository.findByEmail(email);
}
#Override
public User addUser(UserRegistrationDto user) throws EmailExistsException {
if (checkEmailExists(user.getEmail())) {
throw new EmailExistsException("There is an account with that email adress: " + user.getEmail());
} else {
User userObject = new User();
userObject.setEmail(user.getEmail());
userObject.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
userObject.setFirstName(user.getFirstName());
userObject.setLastName(user.getLastName());
userObject.setPhoneNumber(user.getPhoneNumber());
userObject.setAddress(user.getAddress());
userObject.setJoinedDate(DateUtils.todayStr());
userObject.setRoles(new HashSet<>(roleRepository.findAll()));
userRepository.save(userObject);
return userObject;
}
}
#Override
public void updateUser(String email, User userUpdate) {
User toUpdate = userRepository.findByEmail(email);
if(toUpdate.getEmail().equals(userUpdate.getEmail())){
userRepository.save(userUpdate);
}
}
private boolean checkEmailExists(String email) {
User user = null;
user = userRepository.findByEmail(email);
if (user != null) {
return true;
} else {
return false;
}
}
}
After entering the data in the form, a database query appears. UserDetailsService also does not throw an error that the user was not found. So what could the error be in?
On the first sight, shouldn't you inject UserServiceDAO instead of UserService in 'CustomUserDetailsService' class?
I am trying to search data from H2 database using elasticsearch. elasticsearch work fine in my project.I added elasticsearch externally.when i add data using constructor it adds to elasticsearch.but if try to add data from H2 database to elasticsearch I am getting this error.
-Error creating bean with name 'itemMigrator' defined in file.
-Error creating bean with name 'userJpaRepository': Invocation of init method failed;
-No property findAll found for type User!
spring-boot-starter-parent 2.0.6.RELEASE
elasticsearch version-5.6.12
I am following this example https://www.hameister.org/SpringBootElasticsearch.html
http://techprimers.com/spring-data-elastic-search-example-3-using-spring-jpa/
User.java
#Entity
#Document(indexName = "user", type = "user", shards = 1)
#Table(name = "user_detail")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#NotBlank
#Column(name = "first_name")
private String firstName;
#NotBlank
#Column(name = "last_name")
private String lastName;
SearchQueryBuilder.java
#Component
public class SearchQueryBuilder {
private ElasticsearchTemplate elasticsearchTemplate;
#Autowired
public SearchQueryBuilder(ElasticsearchTemplate elasticsearchTemplate) {
this.elasticsearchTemplate = elasticsearchTemplate;
}
public List<User> getAll(String text) {
QueryBuilder query = QueryBuilders.boolQuery()
.should(
QueryBuilders.queryStringQuery(text)
.lenient(true)
.field("firstName")
.field("role")
).should(QueryBuilders.queryStringQuery("*" + text + "*")
.lenient(true)
.field("firstName")
.field("role"));
NativeSearchQuery build = new NativeSearchQueryBuilder()
.withQuery(query)
.build();
List<User> userses = elasticsearchTemplate.queryForList(build, User.class);
return userses;
}
}
ItemMigrator.java
#Autowired
ElasticsearchOperations operations;
#Autowired
UsersRepository UsersRepository;
#Autowired
UserJpaRepository jpaRepository;
#Autowired
public ItemMigrator(UserJpaRepository jpaRepository, ElasticsearchTemplate operations, UsersRepository UsersRepository) {
this.jpaRepository = jpaRepository;
this.operations = operations;
this.UsersRepository = UsersRepository;
}
#PostConstruct
#Transactional
public void loadAll() {
Iterable<User> items = jpaRepository.findAll();
operations.putMapping(User.class);
UsersRepository.saveAll(items);
}
}
UserJpaRepository.java
public interface UserJpaRepository extends JpaRepository<User, Long>
UsersRepository.java
public interface UsersRepository extends ElasticsearchRepository<User, Long>{
List<User> findByFirstName(String text);
List<User> findByRole(String text);
ManualSearchResource.java
#RestController
#RequestMapping("/rest/manual/search")
public class ManualSearchResource {
#Autowired
private SearchQueryBuilder searchQueryBuilder;
#GetMapping(value = "/{text}")
public List<User> getAll(#PathVariable final String text) {
return searchQueryBuilder.getAll(text);
}
}
Config.java
#Configuration
#EnableJpaRepositories(basePackages = "net.kzn.shoppingbackend")
#EnableElasticsearchRepositories(basePackages = "net.kzn.shoppingbackend")
#ComponentScan(basePackages = { "net.kzn.shoppingbackend" })
public class Config {
#Bean
public Client client() throws UnknownHostException {
Settings settings = Settings.builder()
.put("client.transport.sniff", true)
.put("cluster.name", "elasticsearch").build();
#SuppressWarnings("resource")
TransportClient client = new PreBuiltTransportClient(settings)
.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"), 9300));
return client;
}
#Bean
public ElasticsearchOperations elasticsearchTemplate() throws UnknownHostException {
return new ElasticsearchTemplate(client());
}
}
This is my stacktrace https://www.dropbox.com/s/nxf2a3m961dx7a7/elastic_error.txt?dl=0
Is it correct to add both #Entity and #Document is same class?Please tell me what am i doing wrong here.
i try to perform a login process with spring-boot, oauth2 and spring security. I implemented a custom userdetails service.
Here the code:
#Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
private final UserService userService;
#Autowired
public CustomUserDetailsService(UserService userService) {
this.userService = userService;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.findByUsername(username);
if (user == null)
throw new UsernameNotFoundException(String.format("User %s does not exist!", username));
return new UserRepositoryUserDetails(user);
}
private final static class UserRepositoryUserDetails extends User implements UserDetails {
private static final long serialVersionUID = 1L;
private UserRepositoryUserDetails(User user) {
super(user);
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return getRoles();
}
// another methods
#Override
public boolean isEnabled() { return super.isEnabled(); }
}
}
The user entity:
#Entity
#Table
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "uuid2")
#Column(name = "id", columnDefinition = "VARCHAR(50)")
private String userUUId;
// another parametes
#Column(nullable = false, columnDefinition = "TINYINT DEFAULT false")
#Type(type = "org.hibernate.type.NumericBooleanType")
private boolean enabled;
public User() {
}
public User(User user) {
super();
this.userUUId = user.getUserUUId();
this.roles = user.getRoles();
this.name = user.getName();
this.email = user.getEmail();
this.enabled = isEnabled();
this.password = user.getPassword();
}
// ...
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
The security configuration:
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
And a part of an authorizationserver configuration:
#Configuration
#EnableAuthorizationServer
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Bean(name = "tokenStore")
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore())
.authenticationManager(authenticationManager)
.userDetailsService(customUserDetailsService);
}
Here the error log:
type=AUTHENTICATION_FAILURE, data={type=org.springframework.security.authentication.DisabledException, message=User is disabled}]
[2016-08-25 09:23:17.774] boot - 21158 INFO [http-nio-8443-exec-1] --- TokenEndpoint: Handling error: InvalidGrantException, User is disabled
[2016-08-25 09:23:17.832] boot - 21158 DEBUG [http-nio-8443-exec-1] --- OrderedRequestContextFilter: Cleared thread-bound request context: org.apache.catalina.connector.RequestFacade#6ea0e0af
[2016-08-25 09:23:17.837] boot - 21158 ERROR [http-nio-8443-exec-4] --- EndpointsAuthentification: org.springframework.web.client.HttpClientErrorException: 400 Bad Request
[2016-08-25 09:23:17.839] boot - 21158 DEBUG [http-nio-8443-exec-4] --- OrderedRequestContextFilter: Cleared thread-bound request context: org.apache.catalina.connector.RequestFacade#4afe7f7
[2016-08-25 09:23:17.840] boot - 21158 ERROR [http-nio-8443-exec-4] --- [dispatcherServlet]: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
java.lang.NullPointerException
at com.x.server.controller.LoginController.login(LoginController.java:76)
But i am sure, the user account is enabled. A call of user.isEnabled return true, but the framework cannot detect it.
Any ideas?
Cheers
Probably the enabled field in database is null or false
The org.springframework.security.authentication.DisabledException is thrown when the isEnabled() method of the UserDetails returns false.
From your implementation, User user = userService.findByUsername(username); in the CustomUserDetailsService is fetching from database a user whose enabled property is false.
Find a way to change it to true.
in your UserDetailsImpl class, isEnabled must be return true;
#Override
public boolean isEnabled() {
return true;
}