Good day,
I am using spring security to restrict user, and i am using mongodb.
I have created UserDetail and userDetail Services.
her is my webSecurity config.
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public UserDetailsService mongoUserDetails() {
return new CustomUserDetailsService();
}
#Bean
public DaoAuthenticationProvider authProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(mongoUserDetails());
authProvider.setPasswordEncoder(new BCryptPasswordEncoder());
return authProvider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home", "/static/unsecure/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
}
I am storing permissions in database like this.
"permissions" : [
"/reports/report1",
"/reports/report2"
]
and here is userDetail service.
public class MongoUserDetails implements UserDetails {
private final String username;
private final String password;
private final List<GrantedAuthority> grantedAuthorities;
private final boolean enabled;
public MongoUserDetails(String username, String password, String[] authorities, boolean isEnabled) {
this.username = username;
this.password = password;
this.grantedAuthorities = AuthorityUtils.createAuthorityList(authorities);
this.enabled = isEnabled;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return grantedAuthorities;
}
#Override
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return username;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return enabled;
}
}
in private final List grantedAuthorities; I have successfully stored authorites. which are "/reports/report1", "/reports/report2"
When i run my web application it is redirecting my to login page and after valid credentials it redirects my to /hello page.
Now i am creating controllers and actions like
#Controller
#ResponseBody
public class MyController {
#GetMapping("/user/greeting")
public String greeting(HttpServletRequest request){
return "welcome secure";
}
#GetMapping("/login1")
public String greeting1(HttpServletRequest request){
}
}
so since List grantedAuthorities; does not contains this route it should not allowed the user for this action
how can i achieve this. assuming all authorities are dynamic.
thanks.
My example:
Authentication service:
#Service
public class AuthService implements UserDetailsService {
#Autowired
private UserJpaRepository userJpaRepository;
#Autowired
private UserProfileJPARepository profileJPARepository;
#Autowired
private UserProfileContainer profileContainer;
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = (User) userJpaRepository.findByLogin(username);
GrantedAuthority authority = new SimpleGrantedAuthority(user.getRole().name());
UserDetails userDetails = (UserDetails) new SecureUser(
user.getId(),
user.getFirstName(),
user.getLastName(),
user.getEmail(),
user.getMobileNumber(),
user.getLogin(),
user.getPassword(),
user.isActive(),
true,
true,
true,
Arrays.asList(authority)
);
user.setLastLogonTime(new Timestamp((new Date()).getTime()));
userJpaRepository.saveAndFlush(user);
profileContainer.setUser(user);
return userDetails;
}
}
Session scoped bean:
#Component
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserProfileContainer {
private User user = null;
public UserProfileContainer() {
}
public void setUser(User user) {
this.user = user;
}
public User getUser() {
return user;
}
}
And in User class you cal store a List<String> allowedURLS = new ArrayList<>();
AFTER EDIT:
I've wrote this example. In this case I have some SecureUser.class which extends org.springframework.security.core.userdetails.User and in this class I have Set<String> allowedPathSet actually HashSet. And here is the custom filter:
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomerFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
// nothing here...
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpSession session = req.getSession(true);
SecurityContextImpl sci = (SecurityContextImpl) session.getAttribute("SPRING_SECURITY_CONTEXT");
if (sci != null) {
SecureUser user = (SecureUser) sci.getAuthentication().getPrincipal();
String url = req.getRequestURI();
if (!user.path.contains("url")) ((HttpServletResponse) response).sendRedirect("http://redirect-URL-Here/");
}
chain.doFilter(request, response);
}
#Override
public void destroy() {
}
}
This example isn't a silver bullet but the main idea is present here and it work.
Related
I just added WebSecurityConfigurerAdapter to my project to try and make an user logic (login - password - what which user could do to my application) for the first time ever but something is really wrong.
Whenever I try to make a request, to any path or any kind of method, it returns me as 403 Forbidden! I don't know what to do since this is the first time I'm dealing with any kind of security logic.
this is my code:
#Configuration
#EnableWebSecurity
#ComponentScan
#EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter{
#Autowired
private UserDetailsService userDetailsService;
#Bean
AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider provider
= new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(new BCryptPasswordEncoder());
return provider;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/**")
.hasAuthority("ADMIN")
.antMatchers(HttpMethod.DELETE, "/**")
.hasAuthority("ADMIN")
.antMatchers(HttpMethod.PUT, "/**")
.hasAuthority("ADMIN")
.antMatchers(HttpMethod.GET, "/**")
.hasAuthority("ADMIN")
.antMatchers(HttpMethod.GET, "/tools")
.hasAuthority("USER")
.anyRequest()
.authenticated()
.and()
.cors()
.and()
.exceptionHandling()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf()
.disable();
}
}
I also have these two classes (I'm following a tutorial and the guy made these two):
public class CustomUserDetails implements UserDetails {
/**
*
*/
private static final long serialVersionUID = 1L;
private Users user;
public CustomUserDetails(Users user) {
super();
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.singleton(new SimpleGrantedAuthority(user.getRole()));
}
#Override
public String getPassword() {
return user.getPassword();
}
#Override
public String getUsername() {
return user.getLogin();
}
public String getEmail() {
return user.getEmail();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
and
public class CustomUserDetails implements UserDetails {
/**
*
*/
private static final long serialVersionUID = 1L;
private Users user;
public CustomUserDetails(Users user) {
super();
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.singleton(new SimpleGrantedAuthority(user.getRole()));
}
#Override
public String getPassword() {
return user.getPassword();
}
#Override
public String getUsername() {
return user.getLogin();
}
public String getEmail() {
return user.getEmail();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
if I left anything out and you really wanna help, this is the entire code: https://github.com/vitoriaacarvalho/backend-challenge-very-useful-tools-to-remember-
I'm already thanking (so much) anyone who responds and tries to help!
When I look at your code, you do not set the user's privileges in the service that returns user information.
You have created a user in UserService and specified only the id information. In this case, you cannot access any of the urls you have given to the role.
If you edit your code as below, your problem will be solved.
#Service
public class CustomUserDetailsService implements UserDetailsService{
#Autowired
private UserRepository repo;
#Override
public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException {
Optional<Users> user= repo.findByLogin(login);
if(user==null) {
throw new UsernameNotFoundException("User not found");
}
//return new CustomUserDetails(user.get());
return new User(login, login, Collections.singletonList(new SimpleGrantedAuthority("ADMIN")))
}
}
First you have to assign role in REST API. Then login to correct user role.
So, I have two different types of users, a User entity that has a role of USER and a HOST entity that has a role of HOST, and roles have permissions accordingly.
I created a UserPrincipal that implements UserDetails where i override the getAuthorities() method and add a simpleGrantedAuthority.
I created roles and permissions as Enums.
This is the ApplicationUserPermission Enum that specifies the permissions.
public enum ApplicationUserPermission {
USER_READ("user:read"),
USER_WRITE("user:write"),
HOST_READ("host:read"),
HOST_WRITE("host:write"),
APARTMENT_READ("apartment:read"),
APARTMENT_WRITE("apartment:write");
private final String permission;
ApplicationUserPermission(String permission) {
this.permission = permission;
}
public String getPermission() {
return permission;
}
and then i have the ApplicationUserRole that sets these permissions to two different roles: USER and HOST.
public enum ApplicationUserRole {
USER(Sets.newHashSet(USER_READ, USER_WRITE, APARTMENT_READ)),
HOST(Sets.newHashSet(HOST_READ, HOST_WRITE, APARTMENT_READ, APARTMENT_WRITE));
private final Set<ApplicationUserPermission> permissions;
ApplicationUserRole(Set<ApplicationUserPermission> permissions) {
this.permissions = permissions;
}
public Set<ApplicationUserPermission> getPermissions() {
return permissions;
}
Then I have the UserPrincipal that implements UserDetails and sets the authorities for User entity
public class UserPrincipal implements UserDetails {
private final User user;
public UserPrincipal(User user) {
super();
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.singleton(new SimpleGrantedAuthority("ROLE_"+USER.name()));
}
Then I have my custom UserDetailsService that implements UserDetailsService
#Service
public class MyUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Autowired
private HostRepository hostRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
String lastDigits = username.substring(username.length() - 3);
User user;
Host host;
if (username.contains("#")) {
host = hostRepository.findByEmail(username);
if (host == null) {
throw new UsernameNotFoundException("Server 404");
}
return new HostPrincipal(host);
}
user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("Server 404");
}
return new UserPrincipal(user);
}
This is my ApplicationSecurityConfig.java:
#Configuration
#EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
#Qualifier("myUserDetailsService")
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
#Bean
public UserDetailsService userDetailsService() {
return super.userDetailsService();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(10);
}
#Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and() // check if user exist in database and create a token for specific user
.addFilter(new JwtUsernameAndPasswordAuthAuthenticationFilter(authenticationManager()))
.addFilterAfter(new JwtTokenVerifier(), JwtUsernameAndPasswordAuthAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/", "/login", "/createHost", "/createUser", "/getAllApartments").permitAll()
.antMatchers("/users/**").hasRole("USER")
.antMatchers("/hosts/**").hasRole(HOST.name()).anyRequest().authenticated()
.httpBasic().authenticationEntryPoint(authenticationEntryPoint);
}
This is my JwtUsernameAndPasswordAuthFilter.java:
public class JwtUsernameAndPasswordAuthAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private static final Logger logger = LoggerFactory.
getLogger(JwtUsernameAndPasswordAuthAuthenticationFilter.class);
private final AuthenticationManager authenticationManager;
public JwtUsernameAndPasswordAuthAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
try {
UsernamePasswordAuthenticationRequest authenticationRequest =
new ObjectMapper().readValue(request.getInputStream(),
UsernamePasswordAuthenticationRequest.class);
Authentication authentication = new UsernamePasswordAuthenticationToken(
authenticationRequest.getUsername(),
authenticationRequest.getPassword()
);
authenticationManager.authenticate(authentication);
return authentication;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
logger.info("Auth result: {}", authResult);
logger.info("Authorities: {}", authResult.getAuthorities());
String key = "securesecuresecuresecuresecuresecuresecuresecuresecuresecuresecure";
String token = Jwts.builder()
.setSubject(authResult.getName())
.claim("authorities", authResult.getAuthorities())
.setIssuedAt(Date.valueOf(LocalDate.now().plusWeeks(2)))
.signWith(Keys.hmacShaKeyFor(key.getBytes()))
.compact();
String tokenAsJson = new JSONObject()
.put("token", token)
.toString();
response.addHeader("Authorization", "Bearer " + token);
response.setContentType("application/json");
response.getWriter().write(tokenAsJson);
}
EDIT: This is my Authorization filter
public class JwtTokenVerifier extends BasicAuthenticationFilter {
public JwtTokenVerifier(AuthenticationManager authenticationManager){
super(authenticationManager);
}
#Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String authorizationHeader = request.getHeader("Authorization");
if (Strings.isNullOrEmpty(authorizationHeader) || !authorizationHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
String secretKey = "securesecuresecuresecuresecuresecuresecuresecuresecuresecuresecure";
String token = authorizationHeader.replace("Bearer ", "");
try {
Jws<Claims> claimsJws = Jwts.parser().
setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes())).parseClaimsJws(token);
Claims body = claimsJws.getBody();
String username = body.getSubject();
var authorities = (List<Map<String, String>>) body.get("authorities");
Set<SimpleGrantedAuthority> simpleGrantedAuthoritySet = authorities.stream()
.map(m -> new SimpleGrantedAuthority(m.get("authority"))).collect(Collectors.toSet());
Authentication authentication = new UsernamePasswordAuthenticationToken(username,
null, simpleGrantedAuthoritySet);
SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (JwtException e) {
throw new IllegalStateException(String.format("Token %s cannot be trusted", token));
}
filterChain.doFilter(request, response);
}
}
authResult.getAuthorities() returns empty [].
If I try to access "/users/**" after I logged in as a User it returns 403 Forbidden.
Am using spring boot 2.2.4 with spring security. And the problem am facing is every time am logging in with a new user the principal username is getting reset to the newly logged in user's details, Why. However as I have observed the session ID remains correct. I don't understand this behavior. Any idea ?
#EnableWebSecurity
public class IctSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
PasswordEncoder passwordEncoder;
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//URLs matching for access rights
.antMatchers("/").permitAll()
.antMatchers("/signup").permitAll()
.antMatchers("/signin").permitAll()
.antMatchers("/ict/**/**").permitAll()
.antMatchers("/user/queue/reply").hasAnyAuthority(RolesConst.APP_USER)
.antMatchers("/find").hasAnyAuthority(RolesConst.APP_USER)
//.anyRequest().authenticated()
.and()
//form login
.csrf().disable().formLogin()
.loginPage("/login")
.failureUrl("/login?error=true")
.defaultSuccessUrl("/dashboard")
.and()
//logout
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login").and()
.exceptionHandling()
.accessDeniedPage("/access-denied");
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
//return NoOpPasswordEncoder.getInstance();
}
}
UserDetails service
#Service
public class IctUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//return new IctUserDetails(username);
Optional<UserEntity> user = userRepository.findByUsername(username);
user.orElseThrow(() -> new UsernameNotFoundException("Not found: " + username));
//UserController.groupname(username);
return user.map(IctUserDetails::new).get();
}
}
UserDetails class
public class IctUserDetails implements UserDetails {
private static final long serialVersionUID = 1L;
public static String username;
private String password;
private List<? extends GrantedAuthority> authorities;
public IctUserDetails() {
}
public IctUserDetails(UserEntity userEntity) {
this.username = userEntity.getUsername();
this.password = userEntity.getPassword();
List<SimpleGrantedAuthority> authoriteis = getRoleAuthoritiesFromUserEntity(userEntity);
this.authorities = authoriteis;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
#Override
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return this.username;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
/**
* Get list of role authorities from current user entity
* #param userRoleEntities
* #return
*/
#Transactional
private List<SimpleGrantedAuthority> getRoleAuthoritiesFromUserEntity(UserEntity userEntity) {
Collection<UserRoleEntity> userRoleEntities = userEntity.getUserRoleEntities();
List<SimpleGrantedAuthority> authoriteis = userRoleEntities
.stream()
.map(ur -> ur.getRole().getRoleName())
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
return authoriteis;
}
}
So, this was the problem. I had declared username in my UserDetails class as static
public static String username;.
I have changed this to private String username; and all is good now. Thanks.
After configuring a custom user details service that uses an embedded H2 database to retrieve user credentials, I keep receiving 401 Unauthorized errors when completing a post command even though the username and password are available. I don't know if this makes any difference, but the console still prints out the generated security password (although the auto generated credentials return the same 401 error). Please take a look at my code below and let me know any suggestions or fixes available.
User model...
#Entity
public class ApplicationUser {
#Id
private Long id;
private String username, password, role;
public ApplicationUser(String username, String role, String password) {
this.username=username;
this.password=password;
this.role=role;
}
public ApplicationUser() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
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;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
#Override
public String toString() {
return "ApplicationUser{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", role='" + role + '\'' +
'}';
}
}
User repository class...
#Repository
public class AppUserRepository {
#Autowired
JdbcTemplate jdbcTemplate;
static class ApplicationUserRowMapper implements RowMapper<ApplicationUser> {
#Override
public ApplicationUser mapRow(ResultSet rs, int rowNum) throws SQLException {
ApplicationUser applicationUser = new ApplicationUser();
applicationUser.setUsername(rs.getString("username"));
applicationUser.setPassword(rs.getString("password"));
applicationUser.setRole(rs.getString("userrole"));
return applicationUser;
}
}
public List<ApplicationUser> findAll() {
return jdbcTemplate.query("select u.username, u.password, ur.userrole from ApplicationUsers u, ApplicationUsers_Role ur where u.username = ur.username",
new ApplicationUserRowMapper());
}
public ApplicationUser findByUsername(String username) {
return jdbcTemplate.queryForObject("select u.username, u.password, ur.userrole from ApplicationUsers u, ApplicationUsers_Role ur where u.username = ? and u.username = ur.username",
new Object[] {username}, new ApplicationUserRowMapper());
}
}
Security config class...
#Configuration
#EnableWebSecurity
#ComponentScan(basePackages = "security_package")
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
CustomUserDetailsService customUserDetailsService;
#Autowired
CustomAuthenticationProvider customAuthenticationProvider;
#Bean
public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
#Autowired
public void configureGlobal (AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/login").permitAll().anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").usernameParameter("username").passwordParameter("password");
}
}
Custom authentication provider...
#Service
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
AppUserRepository userRepository;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
ApplicationUser user = userRepository.findByUsername(name);
if(user == null) {
throw new BadCredentialsException("Authentication failed.");
}
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
grantedAuthorities.add(new SimpleGrantedAuthority(user.getRole()));
return new UsernamePasswordAuthenticationToken(name, password, grantedAuthorities);
}
#Override
public boolean supports(Class<?> aClass) {
return aClass.equals(UsernamePasswordAuthenticationToken.class);
}
}
Main app class...
#SpringBootApplication(exclude={
HazelcastAutoConfiguration.class,
DataSourceAutoConfiguration.class,
SecurityAutoConfiguration.class
})
#EnableScheduling
#Slf4j
public class AwsorchestratorApplication implements CommandLineRunner{
#Override
public void run(String... arg0) throws Exception {
if (arg0.length > 0 && arg0[0].equals("exitcode")) {
throw new ExitException();
}
}
public static void main(String[] args) throws Exception {
if ( System.getProperty("spring.profiles.active") == null )
{
System.setProperty("spring.profiles.active","local");
}
new SpringApplication(AwsorchestratorApplication.class).run(args);
}
class ExitException extends RuntimeException implements ExitCodeGenerator {
private static final long serialVersionUID = 1L;
#Override
public int getExitCode() {
return 10;
}
}
}
UPDATE ONE::...
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private AppUserRepository userRepo;
#Override
public UserDetails loadUserByUsername(String username) {
ApplicationUser user = userRepo.findByUsername(username);
if(user == null) {
throw new UsernameNotFoundException("User '" + username + "' not found.");
}
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(user.getRole());
return new User(user.getUsername(), user.getPassword(), Collections.singletonList(grantedAuthority));
}
}
I was able to correct this issue by removing my custom authentication class (although I don't think this was actually part of the problem), and editing my web security config to match the below.
#Configuration
#EnableWebSecurity
#ComponentScan(basePackages = package_location)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public CustomUserDetailsService detailsService;
#Bean
public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
#Autowired
public void configureGlobal (AuthenticationManagerBuilder auth) throws Exception {
//the password encoder here is only to support a mix of encoding - that change can be removed
auth.userDetailsService(detailsService).passwordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.headers()
.frameOptions()
.disable()
.and()
.authorizeRequests()
.antMatchers("/awsorchestrator/**")
.hasAnyRole("ADMIN", "USER")
.and()
.formLogin()
.usernameParameter("username")
.passwordParameter("password")
.and()
.csrf()
.disable();
}
}
The important part that directly correlated to my problem was the passwordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder()) portion. I also had to change the password stored in the database to be prefixed with {bcrypt} and now I am able to login with no issues.
Thanks R.G for the tips.
The successfulAuthentication function in JWTAuthenticationFilter.java gives a nullpointerexception. Do you see why this would be an issue? Is it a problem with using the same bean for autowiring?
This is my current project structure:
-com
-register
-RegisterController.java
-security
-JWTAuthenticationFilter.java
-JWTAuthorizationFilter.java
-SecurityConstants.java
-WebSecurity.java
-user
-User.java
-UserRepository.java
-UserService.java
-Application.java
Application.java
#Configuration
#SpringBootApplication
public class Application {
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
UserRepository.java
#Repository
public interface UserRepository extends MongoRepository<User, String> {
User findByUsername(String name);
User findByEmail(String Email);
User findBy_id(ObjectId id);
}
UserService.java
#Service
public class UserService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = this.userRepository.findByUsername(username);
if(user == null)
return null;
List<SimpleGrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority("user"));
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
}
public User getUserByUsername(String username) {
return this.userRepository.findByUsername(username);
}
public User getUserBy_id(ObjectId _id) {
return userRepository.findBy_id(_id);
}
public void saveUser(User newUser){
userRepository.save(newUser);
}
}
User.java
#Document
public final class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private ObjectId _id;
private String email;
private String username;
private String password;
private AccountProperties accountProperties;
private Address address;
private List<Pet> pets = new ArrayList<>();
private String phoneNumber;
public User() {}
public User(#JsonProperty("email") String email, #JsonProperty("username") String username,
#JsonProperty("password") String password) {
this.email = email;
this.username = username;
this.password = password;
}
public String get_id() { return _id.toHexString();}
getters and setters() ...
}
JWTAuthenticationFilter.java
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
#Autowired
private UserRepository userRepo;
private AuthenticationManager authenticationManager;
JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException {
try {
User creds = new ObjectMapper()
.readValue(req.getInputStream(), User.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword(),
new ArrayList<>())
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#Override
protected void successfulAuthentication(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain,
Authentication auth) throws IOException, ServletException {
String username = ((org.springframework.security.core.userdetails.User) auth.getPrincipal()).getUsername();
String token = JWT.create()
.withSubject(username)
.withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.sign(HMAC512(SECRET.getBytes()));
res.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
User u = uRepo.findByUsername("admin");
res.getWriter().write(
"{\"" + SecurityConstants.HEADER_STRING + "\":\"" + SecurityConstants.TOKEN_PREFIX+token + "\"," +
"\"" + "ObjectID" + "\":\"" + u.get_id() + "\"}"
);
}
}
JWTAuthorizationFilter.java
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
public JWTAuthorizationFilter(AuthenticationManager authManager) {
super(authManager);
}
#Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
if (token != null) {
// parse the token.
String user = JWT.require(Algorithm.HMAC512(SECRET.getBytes()))
.build()
.verify(token.replace(TOKEN_PREFIX, ""))
.getSubject();
if (user != null) {
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
}
WebSecurity.java
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userDetailsService;
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS );
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(this.userDetailsService).passwordEncoder(this.bCryptPasswordEncoder);}
#Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}
SecurityConstants.java
public class SecurityConstants {
public static final String SECRET = "SecretKeyToGenJWTs";
public static final long EXPIRATION_TIME = 864_000_000; // 10 days
public static final String TOKEN_PREFIX = "Bearer ";
public static final String HEADER_STRING = "Authorization";
public static final String SIGN_UP_URL = "/users/sign-up";
}
RegisterController.java
#RestController
#RequestMapping("/users")
public class RegisterController {
#Autowired
private UserService userService;
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#PostMapping("/sign-up")
public void signUp(#RequestBody User user) {
if (user.getPassword() == null || user.getUsername() == null)
return;
user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
userService.saveUser(user);
}
}
Not sure if this is the root cause of your issue, but I have never seen #Configuration in the main application. I would try to move that to a separate config class and see if that helps
Annotate the JWTAuthenticationFilter with #Component or add #Bean in configuration file . Looks like the object is not created
The problem is that you doesn't define JWTAuthenticationFilter as Bean, so spring doesn't inject dependencies in it.
You can get beans in filter manualy. From the GenericFilterBean javadoc:
This generic filter base class has no dependency on the Spring org.springframework.context.ApplicationContext concept. Filters usually don't load their own context but rather access service beans from the Spring root application context, accessible via the filter's ServletContext (see org.springframework.web.context.support.WebApplicationContextUtils).
Or you can make it bean. But if you are using Spring Boot consider that:
The fact that all filters internal to Spring Security are unknown to the container is important, especially in a Spring Boot application, where all #Beans of type Filter are registered automatically with the container by default. So if you want to add a custom filter to the security chain, you need to either not make it a #Bean or wrap it in a FilterRegistrationBean that explicitly disables the container registration.