Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
Ok so I am building an ecommerce site using java spring mvc, spring security, maven, hibernate, and some other technologies however those should be the only ones relevant to my problem. I am doing all java config with no XML by the way (which I am currently considering revising since I am running into situations where xml just seems easier).
I would like to know why I am getting an no exceptions upon maven install but getting exceptions on server restart (tomcat 7).
I have tried to get this down to as simple as possible while still having a user and a role. When I am looking at the code, it really is pretty straight forward stuff - I am dying to find out why it errors out everytime.
Main Appconfig class
#EnableWebMvc
#Configuration
#ComponentScan({ "com.crutchsf.*" })
#EnableTransactionManagement
#Import({ SecurityConfig.class })
public class AppConfig {
#Bean
public SessionFactory sessionFactory() {
LocalSessionFactoryBuilder builder = new LocalSessionFactoryBuilder(dataSource());
builder
.scanPackages("com.crutchsf.users.model")
.addProperties(getHibernateProperties());
return builder.buildSessionFactory();
}
private Properties getHibernateProperties() {
Properties prop = new Properties();
prop.put("hibernate.format_sql", "true");
prop.put("hibernate.show_sql", "true");
prop.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
return prop;
}
#Bean(name = "dataSource")
public BasicDataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/test");
ds.setUsername("bobby");
ds.setPassword("password");
return ds;
}
#Bean
public HibernateTransactionManager txManager() {
return new HibernateTransactionManager(sessionFactory());
}
#Bean
public UserService userService() {
UserService userService = new MyUserDetailsService();
userService.setUserDao(new UserDaoImpl());
return userService;
}
#Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("validation");
return messageSource;
}
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/pages/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/img/**").addResourceLocations("/img/").setCachePeriod(31556926);
}
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
SecurityConfig.java
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("userDetailsService")
UserDetailsService userDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/admin/**")
.access("hasRole('ROLE_ADMIN')").and().formLogin()
.loginPage("/login").failureUrl("/login?error")
.usernameParameter("username")
.passwordParameter("password")
.and().logout().logoutSuccessUrl("/login?logout")
.and().csrf()
.and().exceptionHandling().accessDeniedPage("/403");
}
}
User class
#Entity
#Table(name = "users", catalog = "test")
public class User {
private Integer user_id;
private String username;
private String password;
private String passwordConf;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
#Id
#Column(name = "user_id")
#GeneratedValue
public Integer getUserId() {
return this.user_id;
}
#Column(name = "username", unique = true, nullable = false, length = 45)
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
#Column(name = "password", nullable = false, length = 60)
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
#Transient
public String getPasswordConf() {
return this.passwordConf;
}
public void setPasswordConf(String passwordConf) {
this.passwordConf = passwordConf;
}
}
UserRole class
#Entity
#Table(name = "user_roles", catalog = "test",
uniqueConstraints = #UniqueConstraint(
columnNames = { "role", "username" }))
public class UserRole{
private Integer userRoleId;
private User user;
private String role;
public UserRole() {
}
public UserRole(User user, String role) {
this.user = user;
this.role = role;
}
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "user_role_id",
unique = true, nullable = false)
public Integer getUserRoleId() {
return this.userRoleId;
}
public void setUserRoleId(Integer userRoleId) {
this.userRoleId = userRoleId;
}
public User getUser() {
return this.user;
}
public void setUser(User user) {
this.user = user;
}
#Column(name = "role", nullable = false, length = 45)
public String getRole() {
return this.role;
}
public void setRole(String role) {
this.role = role;
}
}
UserDao Interface
public interface UserDao {
User findByUserName(String username);
List<User> findAllUsers();
void addUser(User user);
}
UserDaoImpl
#Repository
public class UserDaoImpl implements UserDao {
#Autowired
private SessionFactory sessionFactory;
#SuppressWarnings("unchecked")
public User findByUserName(String username) {
List<User> users = new ArrayList<User>();
users = sessionFactory.getCurrentSession().createQuery("from User where username=?").setParameter(0, username)
.list();
if (users.size() > 0) {
return users.get(0);
} else {
return null;
}
}
#Override
#Transactional
public void addUser(User user) {
sessionFactory.getCurrentSession().persist(user);
}
UserService Interface
public interface UserService {
UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException;
void setUserDao(UserDao userDao);
void addUser(com.crutchsf.model.users.User user);
}
User Service Impl
#Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService, UserService {
#Autowired
private UserDao userDao;
#Transactional(readOnly=true)
#Override
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
com.crutchsf.model.users.User user = userDao.findByUserName(username);
List<GrantedAuthority> authorities = buildUserAuthority(user.getUserRole());
return buildUserForAuthentication(user, authorities);
}
private User buildUserForAuthentication(com.crutchsf.model.users.User user, List<GrantedAuthority> authorities) {
return new User(user.getUsername(), user.getPassword(), user.isEnabled(), true, true, true, authorities);
}
private List<GrantedAuthority> buildUserAuthority(Set<UserRole> userRoles) {
Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
// Build user's authorities
for (UserRole userRole : userRoles) {
setAuths.add(new SimpleGrantedAuthority(userRole.getRole()));
}
List<GrantedAuthority> Result = new ArrayList<GrantedAuthority>(setAuths);
return Result;
}
#Override
#Transactional
public void addUser(com.crutchsf.model.users.User user) {
this.userDao.addUser(user);
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
Database SQL used
CREATE TABLE users (
user_id int(11) NOT NULL AUTO_INCREMENT,
username VARCHAR(45) NOT NULL UNIQUE,
PRIMARY KEY (user_id));
Ok I shortened this quite a bit to the simple task of persisting a user. I cant get the server to startup without errors. There has to be something wrong in the code above.
In AppConfig.java if I change the line "scanPackages...." to the actual class name
com.crutchsf.users.model.User
then my server starts up fine but when it reaches the line in my userDao to be persisted
sessionFactory.getCurrentSession().persist(user);
I get a different error from hibernate "Cant find entity com.crutchsf.users.model.User"
Here is the main error I get. Basically I can choose which error I want to get now.
[public org.hibernate.SessionFactory com.crutchsf.config.AppConfig.sessionFactory()] threw exception; nested exception is java.lang.NullPointerException
thank you
On the surface, this doesn't appear to be anything to do with Spring MVC or Hibernate.
The error message is:
Factory method [public org.hibernate.SessionFactory com.crutchsf.config.AppConfig.sessionFactory()] threw exception; nested exception is java.lang.NullPointerException
If you have a stacktrace, you'll be able to see exactly what line the null reference occurred on. If not, you'll have to fire up a debugger (or insert println statements) to find out.
Either way, it appears that your AppConfig class has a bug in it, or you are not initialising it correctly. Look at the sessionConfig() method to see why it NPEs, and ensure that the appropriate references are not null before calling it.
Related
I want only users with role "Admin" to be able to get the "Welcome Admin!" message:
#GetMapping("/admin")
public String admin() {
return "Welcome Admin!";
}
So I added .antMatchers("/admin").access("hasRole('ADMIN')") to my SecurityConfig:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService userDetailsService;
#Autowired
private JwtFilter jwtFilter;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors();
http.csrf().disable().authorizeRequests()
.antMatchers("/authenticate", "/users/register","/products").permitAll()
.antMatchers("/admin").access("hasRole('ADMIN')") // HERE IT SHOULD CHECK FOR THE ADMIN ROLE
.anyRequest().authenticated().and().exceptionHandling()
.and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);;
}
}
The user that I use to access also has the role Admin:
But I always get 403 Forbidden as response:
Instead of adding the .antMatchers("/admin").access("hasRole('ADMIN')") to my SecurityConfig, I also tried to restrict the access with Annotations:
#PreAuthorize("hasRole('ROLE_ADMIN')")
//or #Secured({"ROLE_ADMIN"})
#GetMapping("/admin")
public String admin() {
return "Welcome Admin!";
}
But then I can access it with any user, even if they don't have the Admin Role (response status = 200). What am I doing wrong here?
Additional info:
My UserDetailsService:
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Autowired
private RoleRepository roleRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = null;
try {
user = userRepository.findByName(username);
} catch(Exception e){
String message = e.getMessage();
System.out.println(message);
}
if (user == null) {
return new org.springframework.security.core.userdetails.User(
" ", " ", true, true, true, true,
getAuthorities(Arrays.asList(
roleRepository.findByName("ROLE_USER"))));
}
return new org.springframework.security.core.userdetails.User(user.getName(), user.getPassword(), user.isEnabled(), true, true, true, getAuthorities(user.getRoles()));
}
private Collection<? extends GrantedAuthority> getAuthorities(
Collection<Role> roles) {
return getGrantedAuthorities(getPrivileges(roles));
}
private List<String> getPrivileges(Collection<Role> roles) {
List<String> privileges = new ArrayList<>();
List<Privilege> collection = new ArrayList<>();
for (Role role : roles) {
collection.addAll(role.getPrivileges());
}
for (Privilege item : collection) {
privileges.add(item.getName());
}
return privileges;
}
private List<GrantedAuthority> getGrantedAuthorities(List<String> privileges) {
List<GrantedAuthority> authorities = new ArrayList<>();
for (String privilege : privileges) {
authorities.add(new SimpleGrantedAuthority(privilege));
}
return authorities;
}
public UserDetails loadUserByEmail(String email) throws UsernameNotFoundException {
User user = userRepository.findByEmail(email);
return new org.springframework.security.core.userdetails.User(user.getName(), user.getPassword(), new ArrayList<>());
}
}
My jwtFilter.java:
#Component
public class JwtFilter extends OncePerRequestFilter {
#Autowired
private JwtUtil jwtUtil;
#Autowired
private CustomUserDetailsService service;
#Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
System.out.println("header:" + httpServletRequest.getHeader("Authorization"));
//get Authorization information from the request itself
String authorizationHeader = httpServletRequest.getHeader("Authorization");
String token = null;
String userName = null;
//check for its type, it must be Bearer + jwt
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
//get the token itself
token = authorizationHeader.substring(7);
//decrypt username
userName = jwtUtil.extractUsername(token);
}
if (userName != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = service.loadUserByUsername(userName);
if (jwtUtil.validateToken(token, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); // HERE IS THE getAuthorities function
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}
I'm creating the roles "Admin" and "User" in an extra class and also assign privileges to them as shown in this tutorial.
But actually I never need the privileges, I just couldn't get it done without them, so I left them in. Because without the privileges, I don't know how to get the GrantedAuthorities into my userDetails which are needed for the userDetails.getAuthorities() function in my jwtFilter class...:
#Component
public class SetupDataLoader implements
ApplicationListener<ContextRefreshedEvent> {
boolean alreadySetup = false;
#Autowired
private UserRepository userRepository;
#Autowired
private UserService userService;
#Autowired
private RoleRepository roleRepository;
#Autowired
private PrivilegeRepository privilegeRepository;
#Autowired
private PasswordEncoder passwordEncoder;
#Override
#Transactional
public void onApplicationEvent(ContextRefreshedEvent event) {
if (alreadySetup)
return;
Privilege readPrivilege
= createPrivilegeIfNotFound("READ_PRIVILEGE");
Privilege writePrivilege
= createPrivilegeIfNotFound("WRITE_PRIVILEGE");
List<Privilege> adminPrivileges = Arrays.asList(
readPrivilege, writePrivilege);
createRoleIfNotFound("ROLE_ADMIN", adminPrivileges);
createRoleIfNotFound("ROLE_USER", Arrays.asList(readPrivilege));
Role adminRole = roleRepository.findByName("ROLE_ADMIN");
Role userRole = roleRepository.findByName("ROLE_USER");
User harald = new User();
harald.setName("Harald");
harald.setPassword(passwordEncoder.encode("test"));
harald.setEmail("test#test.com");
harald.setRoles(Arrays.asList(adminRole));
harald.setEnabled(true);
userRepository.save(harald);
User hartmut = new User();
hartmut.setName("Hartmut");
hartmut.setPassword(passwordEncoder.encode("test"));
hartmut.setEmail("test#test.com");
hartmut.setRoles(Arrays.asList(adminRole));
hartmut.setEnabled(true);
userRepository.save(hartmut);
}
alreadySetup = true;
}
#Transactional
Privilege createPrivilegeIfNotFound(String name) {
Privilege privilege = privilegeRepository.findByName(name);
if (privilege == null) {
privilege = new Privilege(name);
privilegeRepository.save(privilege);
}
return privilege;
}
#Transactional
Role createRoleIfNotFound(
String name, Collection<Privilege> privileges) {
Role role = roleRepository.findByName(name);
if (role == null) {
role = new Role(name);
role.setPrivileges(privileges);
roleRepository.save(role);
}
return role;
}
}
My Role.java :
#Entity
#Table(name="roles")
#Data
#NoArgsConstructor
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "roles_privileges",
joinColumns = #JoinColumn(
name = "role_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(
name = "privilege_id", referencedColumnName = "id"))
private Collection<Privilege> privileges;
public Role(String name) {
this.name = name;
}
#Override
public String toString() {
return "Role{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
and my privilege.java:
#Entity
#Table(name="privileges")
#Data
#NoArgsConstructor
public class Privilege {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
public Privilege(String name) {
this.name = name;
}
#Override
public String toString() {
return "Privilege{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
I was able to fix the issue by prepending ROLE_
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_" + ADMIN));
1/ You need to enable the security annotation in your security config with adding this line :
#EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
prePostEnabled let you use #PreAuthorize,#PostAuthorize,etc and securedEnabled for using #Secured
2/ In order to say that only users with the role admin can access "/admin" you need to add an annotation like this :
`
#PreAuthorize("hasRole('ADMIN')")
// or #Secured({"ROLE_ADMIN"})
// or #PreAuthorize("hasAuthority('ROLE_ADMIN')")
#GetMapping("/admin")
public String admin() {
return "Welcome Admin!";
}
.antMatchers("/admin").access("hasRole('ADMIN')") should work too instead of using annotations.
3/ You need to be sure that your method loadByUsername returns the user admin when logging with his authorities list containing the authority "ROLE_ADMIN".
Annotate public class SecurityConfig extends WebSecurityConfigurerAdapter with #EnableGlobalMethodSecurity(prePostEnabled = true)
And,
#PreAuthorize("hasAuthority('ROLE_ADMIN')")
#GetMapping("/admin")
public String admin() {
return "Welcome Admin!";
}
I have implemented a spring boot application with oauth2. when I am trying to access token by providing clientId and Secret then unauthorized(401) response is returned.
oauth_client_detals table is designed in the oracle database with the following schema and secret column value is stored in BCrypt format.
insert into oauth_client_details(client_id,client_secret,web_server_redirect_uri,
scope,accsess_token_validity,refresh_token_validity,resource_id,authorized_grant_types,authorities,
additional_information,autoapprove) values ('web','{bcrypt}$2y$12$FCIQkEmh7ai/6oP99yNOEuWnKt9OjrGEczCxnEnFGDRSOHumOChQO',
'','READ,WRITE','900','3600','','password,authorization_code,refresh_token,implicit','ROLE_ADMIN,ROLE_USER,ROLE_MANAGER','','');
AuthorizationConfig.class
#Configuration
#EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private DataSource dataSource;
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("isAuthenticated()").tokenKeyAccess("permitAll()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource).passwordEncoder(new BCryptPasswordEncoder());
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
}
SecurityConfig.class
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private AuthEntryPoint authEntryPoint;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider1());
}
private AuthenticationProvider authenticationProvider1()
{
DaoAuthenticationProvider provider=new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(new BCryptPasswordEncoder());
return provider;
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().exceptionHandling().authenticationEntryPoint(authEntryPoint)
.and().authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
}
UserDetailsServiceImpl.class
#Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserDAO userDAO;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user= userDAO.findByUserName(username)
.orElseThrow(()->new UsernameNotFoundException("data not found with "+username));
return AuthUserDetails.builder(user);
}
}
AuthUserDetails.class
public class AuthUserDetails implements UserDetails{
private String userName;
private String password;
private List<GrantedAuthority> authorities;
private boolean accNonExpired;
private boolean accNonLocked;
private boolean credentialNonExpired;
private boolean active;
public AuthUserDetails()
{
}
public AuthUserDetails(boolean active, List<GrantedAuthority> authorities, String userName, String password,
boolean accNonExpired, boolean credentialNonExpired, boolean accNonLocked) {
this.active = active;
this.authorities = authorities;
this.userName = userName;
this.password = password;
this.accNonExpired = accNonExpired;
this.credentialNonExpired = credentialNonExpired;
this.accNonLocked = accNonLocked;
}
public static UserDetails builder(User user)
{
List<GrantedAuthority> grantedAuthorities=new ArrayList<>();
user.getRoles().forEach(role-> {
grantedAuthorities.add(new SimpleGrantedAuthority(role.getName().name()));
role.getPermissions().forEach(perm->{
grantedAuthorities.add(new SimpleGrantedAuthority(perm.getName().name()));
});
});
return new AuthUserDetails((user.getActive()==1),grantedAuthorities,user.getUserName(),user.getPassword(),
(user.getAccNonExpired()==1), (user.getCredentialNonExpired()==1),(user.getAccNonLocked()==1));
}
#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 accNonExpired;
}
#Override
public boolean isAccountNonLocked() {
return accNonLocked;
}
#Override
public boolean isCredentialsNonExpired() {
return credentialNonExpired;
}
#Override
public boolean isEnabled() {
return active;
}
}
User.class
#Entity
#Table(name="user56",schema = Schema.OAUTH2,uniqueConstraints = #UniqueConstraint(
columnNames = "username"
))
#Getter
#Setter
public class User {
#Id
#SequenceGenerator(name="user_id_gen",sequenceName = Schema.OAUTH2+".user_id_seq",initialValue = 1003,allocationSize = 1)
#GeneratedValue(generator = "user_id_gen",strategy = GenerationType.SEQUENCE)
#Column(name = "user_id")
private int userId;
#Column(name = "username")
private String userName;
#Column(name = "password")
private String password;
#Column(name = "email")
private String email;
#Column(name = "active")
private int active;
#Column(name = "acc_non_expired")
private int accNonExpired;
#Column(name = "credential_non_expired")
private int credentialNonExpired;
#Column(name = "acc_non_locked")
private int accNonLocked;
#ManyToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinTable(name = "role_user",
joinColumns = {#JoinColumn(name = "user_id",referencedColumnName = "user_id")},
inverseJoinColumns = {#JoinColumn(name = "role_id",referencedColumnName = "id")})
private Set<Role> roles;
}
OAuthClient.class
#Entity
#Table(name = "oauth_client_details",schema = Schema.OAUTH2)
#Getter
#Setter
public class OAuthClient {
#Id
#Column(name = "client_id")
private String clientId;
#Column(name="client_secret")
private String clientSecret;
#Column(name = "web_server_redirect_uri")
private String webServerRedirectUri;
#Column(name = "scope")
private String scope;
#Column(name = "accsess_token_validity")
private String accessTokenValidity;
#Column(name = "refresh_token_validity")
private String refreshTokenValidity;
#Column(name = "resource_id")
private String resourceId;
#Column(name="authorized_grant_types")
private String authorizedGrantType;
#Column(name = "authorities")
private String authorities;
#Column(name = "additional_information")
private String additionalInformation;
#Column(name = "autoapprove")
private String autoApprove;
}
response unauthorized(401) through postman
Updated
AuthEntryPoint.class
#Component
public class AuthEntryPoint implements AuthenticationEntryPoint {
Logger ERROR_LOGGER= LoggerFactory.getLogger(AuthEntryPoint.class);
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
ERROR_LOGGER.error("Unauthorized error : {}",authException.getMessage());
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,"Error : Unauthorized");
}
}
Updated 2
when I tried to run the application under the debugging mode then the following error will occur
FileNotFoundException#769
I have downloaded you project from here and, initially, I only have found some typo in the columns: accsess_token_validity and resource_id
After that I have added the required tables of your project and include some dummy information. For that reason, I'm pretty sure your problem is related with the password values in your oauth_client_details and user56 tables, because only when the stored value did not match with the expected one, I received 401.
Take into account you have defined two different BCryptPasswordEncoder instances:
provider.setPasswordEncoder(new BCryptPasswordEncoder()) for the users included in user56.
clients.jdbc(dataSource).passwordEncoder(new BCryptPasswordEncoder()) for the users included in oauth_clients_detail
Once I fixed it and store the expected ones, I achieved the endpoint returned the expected result:
As I told you in previous comments, there several classes will help you to find the cause of the error:
BasicAuthenticationFilter will get from the request the Basic Auth provided and executes the authentication process.
BasicAuthenticationConverter will extract Basic Auth provided from the request really.
JbdcClientDetailsService will get from oauth_clients_detail the information related with provided client_id in Basic Auth.
The following pictures are the most important to verify if your passwords match. That method will invoke twice: first for client_id / client_pass and second for username / password.
I have included some useful information in the "debug area"
Finally, I have found the place where the problem is occurring, When I send the request to the server then unauthorized(401) error occurs with the following message,
Encoded password does not look like BCrypt
So I have replaced the following code in SecurityConfig.class
#Bean
public PasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}
// replace to
#Bean
public PasswordEncoder getPasswordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
It works fine but still, I couldn't find why BCrytptPasswordEncoder is not worked even secret values are stored in BCrypt format. Anyway thank you very much #doctore for your answer
I successfully build in-memory authentication. But when I going to build it with database comes this error.
There is no PasswordEncoder mapped for the id "null"
This is followed tutorial - Spring Boot Tutorial for Beginners, 10 - Advanced Authentication using Spring Security | Mighty Java
There are classes
SpringSecurityConfiguration.java
#Configuration
#EnableWebSecurity
public class SpringSecurityConfiguration extends
WebSecurityConfigurerAdapter{
#Autowired
private AuthenticationEntryPoint entryPoint;
#Autowired
private MyUserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().httpBasic()
.authenticationEntryPoint(entryPoint);
}
}
AuthenticationEntryPoint.java
#Configuration
public class AuthenticationEntryPoint extends BasicAuthenticationEntryPoint{
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
response.addHeader("WWW-Authenticate", "Basic realm -" +getRealmName());
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
PrintWriter writer = response.getWriter();
writer.println("Http Status 401 "+authException.getMessage());
}
#Override
public void afterPropertiesSet() throws Exception {
setRealmName("MightyJava");
super.afterPropertiesSet();
}
}
MyUserDetailsService .java
#Service
public class MyUserDetailsService implements UserDetailsService{
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if(user == null){
throw new UsernameNotFoundException("User Name "+username +"Not Found");
}
return new org.springframework.security.core.userdetails.User(user.getUserName(),user.getPassword(),getGrantedAuthorities(user));
}
private Collection<GrantedAuthority> getGrantedAuthorities(User user){
Collection<GrantedAuthority> grantedAuthority = new ArrayList<>();
if(user.getRole().getName().equals("admin")){
grantedAuthority.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
}
grantedAuthority.add(new SimpleGrantedAuthority("ROLE_USER"));
return grantedAuthority;
}
}
UserRepository interface
public interface UserRepository extends JpaRepository<User, Long>{
#Query("FROM User WHERE userName =:username")
User findByUsername(#Param("username") String username);
}
Role.java
#Entity
public class Role extends AbstractPersistable<Long>{
private String name;
#OneToMany(targetEntity = User.class , mappedBy = "role" , fetch = FetchType.LAZY ,cascade = CascadeType.ALL)
private Set<User> users;
//getter and setter
}
User.java
#Entity
public class User extends AbstractPersistable<Long>{
//AbstractPersistable class ignore primary key and column annotation(#Column)
private String userId;
private String userName;
private String password;
#ManyToOne
#JoinColumn(name = "role_id")
private Role role;
#OneToMany(targetEntity = Address.class, mappedBy = "user",fetch= FetchType.LAZY ,cascade =CascadeType.ALL)
private Set<Address> address; //Instead of Set(Unordered collection and not allow duplicates) we can use list(ordered and allow duplicate values) as well
//getter and setter}
If you have any idea plese inform. Thank you.
I changed MyUserDetailsService class adding passwordEncoder method.
Added lines
BCryptPasswordEncoder encoder = passwordEncoder();
Changed Line
//changed, user.getPassword() as encoder.encode(user.getPassword())
return new org.springframework.security.core.userdetails.User(--)
MyUserDetailsService.java
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
BCryptPasswordEncoder encoder = passwordEncoder();
User user = userRepository.findByUsername(username);
if(user == null){
throw new UsernameNotFoundException("User Name "+username +"Not Found");
}
return new org.springframework.security.core.userdetails.User(user.getUserName(),encoder.encode(user.getPassword()),getGrantedAuthorities(user));
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
As from Spring Security 5.x, Spring Security enforces you to use a password encoder if you're working with other than in-memory (production) databases.
Spring Security enforces this by activating the default DelegatingPasswordEncoder, which looks for PasswordEncoder beans.
By adding a BCryptPasswordEncoder, the DelegatingPasswordEncoder will return that instance to encrypt passwords.
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
I don't recommend you to do this, but if you really want to, you can override password encoding by adding {noop} to the password value.
This will treat the password by activating the NoOpPasswordEncoder instead of the default DelegatingPasswordEncoder and will treat your password as plain text.
Please note that this is not recommended if you deploy your app to a production environment!
I am actually beginner in Spring framework. For today i've faced with problems and i want ask you a couple questions to figure it out.
I want to make simple user login/authorisation app with Android client.
First of all I want to post my code:
Model:
#Entity
#Table(name = "Users")
public class User {
public User() {
}
#Id
#GeneratedValue(generator = "increment")
#GenericGenerator(name = "increment", strategy = "increment")
#Column(name = "Id", unique = true, nullable = false)
private long id;
#Column(name = "userName", nullable = false)
private String userName;
#Column(name = "userPassword", nullable = false)
private String userPassword;
#Transient
private String confirmPassword;
public long getId() {
return id;
}
public String getUsername() {
return userName;
}
public String getPassword() {
return userPassword;
}
public String getConfirmPassword() {
return confirmPassword;
}
public void setId(long id) {
this.id = id;
}
public void setName(String userName) {
this.userName = userName;
}
public void setPassword(String userPassword) {
this.userPassword = userPassword;
}
public void setConfirmPassword(String confirmPassword) {
this.confirmPassword = confirmPassword;
}
}
Services:
User details:
#Service("userDetailsServiceImpl")
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
#Transactional(readOnly = true)
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
User user = userRepository.findByUserName(userName);
Set<GrantedAuthority> grantedAuthorities = new HashSet<GrantedAuthority>();
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantedAuthorities);
}
}
Impl of UserSerice interface:
#Service
public class UserServiceImpl implements UserService {
#Autowired
private UserRepository userRepository;
#Autowired
private BCryptPasswordEncoder passwordEncoder;
public void save(User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
userRepository.save(user);
}
public User findByUserName(String userName) {
return userRepository.findByUserName(userName);
}
}
User validator:
#Component
public class UserValidator implements Validator {
#Autowired
private UserService userService;
#Override
public boolean supports(Class aClass) {
return User.class.equals(aClass);
}
#Override
public void validate(Object o, Errors errors) {
User user = (User) o;
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "username", "Required");
if (user.getUsername().length() < 8 || user.getUsername().length() > 32) {
errors.rejectValue("username", "Size.userForm.username");
}
if (userService.findByUserName(user.getUsername()) != null) {
errors.rejectValue("username", "Duplicate.userForm.username");
}
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "Required");
if (user.getPassword().length() < 8 || user.getPassword().length() > 32) {
errors.rejectValue("password", "Size.userForm.password");
}
if (!user.getConfirmPassword().equals(user.getPassword())) {
errors.rejectValue("confirmPassword", "Different.userForm.password");
}
}
}
Impl of SecurityService
#Service
public class SecurityServiceImpl implements SecurityService {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private UserDetailsService userDetailsService;
#Override
public String findLoggedInUsername() {
Object userDetails = SecurityContextHolder.getContext().getAuthentication().getDetails();
if (userDetails instanceof UserDetails) {
return ((UserDetails) userDetails).getUsername();
}
return null;
}
#Override
public void autoLogin(String username, String password) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
authenticationManager.authenticate(authenticationToken);
if (authenticationToken.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
}
SecurityConfig:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
#ComponentScan("com.webserverconfig.user")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("userDetailsServiceImpl")
UserDetailsService userDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
PasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
And the lsat one -> Controller:
#RestController
#RequestMapping("/user")
public class UserController {
#Autowired
private UserService userService;
#Autowired
private SecurityService securityService;
#Autowired
private UserValidator userValidator;
#RequestMapping(value = "/registration", method = RequestMethod.POST)
#ResponseBody
#ResponseStatus(value = HttpStatus.CREATED)
public User registration(#RequestBody User user, BindingResult bindingResult, Model model) {
userValidator.validate(user, bindingResult);
if (bindingResult.hasErrors()) {
//What should i return here to my Android client ?
}
userService.save(user);
securityService.autoLogin(user.getUsername(), user.getConfirmPassword());
return user;
}
}
I am missing some classes here for saving space.
I am asking you for help me with my questions, please:
1) When i am trying to send JSON using Postman:
{
"id": 1,
"userName": "Andrew",
"userPassword": "apoyark123",
"confirmPassword": "apoyark123"
}
I am getting next erorr:
DefaultHandlerExceptionResolver - Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: Unrecognized field "userName" (class com.webserverconfig.user.entity.User), not marked as ignorable (4 known properties: "id", "name", "password", "confirmPassword"])
at [Source: java.io.PushbackInputStream#ddd416; line: 3, column: 16] (through reference chain: com.webserverconfig.user.entity.User["userName"]); nested exception is com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "userName" (class com.webserverconfig.user.entity.User), not marked as ignorable (4 known properties: "id", "name", "password", "confirmPassword"])
at [Source: java.io.PushbackInputStream#ddd416; line: 3, column: 16] (through reference chain: com.webserverconfig.user.entity.User["userName"])
Am i missing some annotation ?
This issue resolved by Mehdi in the first comment. !!!
Please help me with second question.
2) I am haven't tested security validation part yet, because this code doesn't working, but i have a question -> How my client will understand that login/authorisation have gone wrong/good ?
If we take a look on controller's method registration(...) -> what should i return to the client if first "if" false ?
What if validation is not correct ? What should i return to the client and how ?
I want to use Spring security with MongoDB (using Spring data) and retrieve the users from my own database for spring security. However, I can not do that since my userservice type does not seem to be supported.
This is my UserService class:
public class UserService {
private ApplicationContext applicationContext;
private MongoOperations mongoOperations;
public UserService() {
applicationContext = new AnnotationConfigApplicationContext(MongoConfig.class);
mongoOperations = (MongoOperations) applicationContext.getBean("mongoTemplate");
}
public User find(String username) {
return mongoOperations.findOne(Query.query(Criteria.where("username").is(username)), User.class);
}
}
And my SecurityConfig class:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserService userService;
#Autowired
public void configAuthBuilder(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(userService); //THIS DOES NOT WORK
builder.inMemoryAuthentication().withUser("username").password("password").roles("USER");
}
}
The line I commented says:
The inferred type UserService is not a valid substitute for the bounded parameter <T extends UserDetailsService>.
How can I fix it so I can retrieve the users from my own database?
Service Layer
You have to create a separate service implementing org.springframework.security.core.userdetails.UserDetailsService and inject it inside the AuthenticationManagerBuilder.
#Component
public class SecUserDetailsService implements UserDetailsService{
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
/*Here add user data layer fetching from the MongoDB.
I have used userRepository*/
User user = userRepository.findByUsername(username);
if(user == null){
throw new UsernameNotFoundException(username);
}else{
UserDetails details = new SecUserDetails(user);
return details;
}
}
}
Model
UserDetails Should be also implemented. This is the POJO which will keep the user authenticated details by the Spring. You may include your Entity data object wrapped inside it, as I have done.
public class SecUserDetails implements UserDetails {
private User user;
public SecUserDetails(User user) {
this.user = user;
}
......
......
......
}
Security Config
Autowire the service that we created before and set it inside the AuthenticationManagerBuilder
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
SecUserDetailsService userDetailsService ;
#Autowired
public void configAuthBuilder(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(userDetailsService);
}
}
Create your own authentication provider providing a class that extends the UserDetailservice.
Ensure content scanning is enable in your spring context xml file.
<authentication-provider user-service-ref="userModelService">
<password-encoder hash="sha" />
</authentication-provider>
#Service
public class UserModelService implements UserDetailsService
{
#Autowired
private UserModelRepositoryImpl repository;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
UserModel user = repository.findByUsername(username);
if( user == null )
throw new UsernameNotFoundException( "Name not found!" );
List<SimpleGrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority( user.getRole()));
return new User(user.getUsername(), user.getSHA1Password(), authorities );
}
public void saveUserDetails(UserModel userModel)
{
repository.save(userModel);
}
}
This class will enable spring query mongo for the username and password required for authentication. Next create the user model class.
public class UserModel
{
private String id;
#Indexed(unique=true)
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
Create the user implementation class that extends the DAO.
#Service
public class UserModelService implements UserDetailsService
{
#Autowired
private UserModelRepositoryImpl repository;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
UserModel user = repository.findByUsername(username);
if( user == null )
throw new UsernameNotFoundException( "Oops!" );
List<SimpleGrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority( user.getRole()));
return new User(user.getUsername(), user.getSHA1Password(), authorities );
}
Finally configure mongo and you're done.