Spring security - Custom authentication provided not called - spring

I have read all stackoverflow topics about same issue. I have read baeldung tutorials, yet I'm still not getting this working.
My CustomAuthenticationProvider is not being called, hence I get denied access everytime.
I might be missing something obvious as I'm a Spring beginner. But I have read plenty of tutorials and I'm pretty sure I'm doing things as it is supposed to be done.
Below is my WebSecurityConfigurerAdapter :
#Configuration
#EnableWebSecurity
public class NovataxewebSecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
CustomAuthenticationProvider customAuthenticationProvider;
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/login/**")
.antMatchers("/resources/**")
.antMatchers("/sessionTimeout")
.antMatchers("/logout");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
/* I was trying to do this at the beginning, since it's not working i'm doing something simpler below
http
.csrf().disable()
.authenticationProvider(customAuthenticationProvider)
.authorizeRequests()
.antMatchers("/login*").anonymous()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/loggedIn")
.failureUrl("/loginfailed")
.and()
.logout().logoutSuccessUrl("/logout")
.deleteCookies("remove")
.invalidateHttpSession(true)
.permitAll()
.and()
.sessionManagement()
.maximumSessions(25);*/
http.authorizeRequests().anyRequest().authenticated()
.and()
.httpBasic();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
}
And here is the CustomAuthenticationProvider. I'm sure it is not going inside thanks to breakpoints inside the authenticate method.
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
private static String loginSave;
private static String passwordSave;
#Autowired
private MessageSource messageSource;
#Autowired
private UsernovaRepository usernovaRepository;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
if(name.matches("")){
throw new UsernameNotFoundException(messageSource.getMessage("utilisateur_incorrect", null, Locale.getDefault()));
}
UsernovaDAO user = null;
try {
user = usernovaRepository.findByUsername(name).get(0);
} catch (Exception e) {
throw new UsernameNotFoundException(messageSource.getMessage("utilisateur_incorrect", null, Locale.getDefault()));
}
String cryptedPass="";
try {
cryptedPass = SHA_256_motdepasse(password);
} catch (Exception e1) {
e1.printStackTrace();
}
if (user!=null && user.getPassword()==null) {
List<GrantedAuthority> grantedAuths = new ArrayList<>();
Authentication auth = new UsernamePasswordAuthenticationToken(name, password, grantedAuths);
try {
user.setPassword(SHA_256_motdepasse(password));
usernovaRepository.saveAndFlush(user);
loginSave = name;
passwordSave = password;
} catch (Exception e) {
e.printStackTrace();
}
return auth;
}else if(user!=null&& user.getPassword().equals(cryptedPass)){
loginSave = name;
passwordSave = password;
List<GrantedAuthority> grantedAuths = new ArrayList<>();
Authentication auth = new UsernamePasswordAuthenticationToken(name, password, grantedAuths);
return auth;
}
else if(user!=null&& !user.getPassword().matches(cryptedPass)){
throw new BadCredentialsException(messageSource.getMessage("mot_de_passe_incorrect", null, Locale.getDefault()));
}
else {
}
throw new UsernameNotFoundException(messageSource.getMessage("utilisateur_incorrect", null, Locale.getDefault()));
}
public String SHA_256_motdepasse(String passW) throws Exception {
// this algorithm returns a sha password
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
Also I'm using Spring Boot with Spring Security. It is an Apache Tomcat Server.

Related

Migration to Ldap based authentication from Inmemory authentication

I have to implement a LDAP based authentication and return a JWT token in response which will be used in subsequent request.
I followed this guide InMemory Authentication with JWT to implement InMemory based Authentication. I know and tried standalone code for LDAP authentication and it's working.
While integrating ldap authentication in the code explained in the above link's example,I am getting stuck and not able to move forward.
I am not getting, How to define loadByUsername method of UserDetailsService class in case of ldap authentication.
Looking for some direction/guidance on this to proceed.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class JWTWebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtUnAuthorizedResponseAuthenticationEntryPoint jwtUnAuthorizedResponseAuthenticationEntryPoint;
#Autowired
private JwtTokenAuthorizationOncePerRequestFilter jwtAuthenticationTokenFilter;
#Autowired
private Environment env;
#Value("${jwt.get.token.uri}")
private String authenticationPath;
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new LdapAuthenticationProvider(env)).eraseCredentials(false);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(jwtUnAuthorizedResponseAuthenticationEntryPoint).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.anyRequest().authenticated();
httpSecurity
.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
httpSecurity
.headers()
.frameOptions().sameOrigin() //H2 Console Needs this setting
.cacheControl(); //disable caching
}
#Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity
.ignoring()
.antMatchers(
HttpMethod.POST,
authenticationPath
)
.antMatchers(HttpMethod.OPTIONS, "/**")
.and()
.ignoring()
.antMatchers(
HttpMethod.GET,
"/" //Other Stuff You want to Ignore
)
.and()
.ignoring()
.antMatchers("/h2-console/**/**");//Should not be in Production!
}
#Bean
#Override
public UserDetailsService userDetailsService() {
return super.userDetailsService();
}
}
#Component
public class LdapAuthenticationProvider implements AuthenticationProvider
{
private Environment environment;
public LdapAuthenticationProvider(Environment environment) {
this.environment = environment;
}
private LdapContextSource contextSource;
private LdapTemplate ldapTemplate;
private void initContext(Authentication authentication)
{ contextSource = new LdapContextSource();
contextSource.setUrl(environment.getProperty("ldap.server.url"));
//contextSource.setAnonymousReadOnly(true);
contextSource.setUserDn("domain\\uid");
contextSource.setBase("DC=global,DC=comp,DC=org");
contextSource.setPassword("pwd");
contextSource.afterPropertiesSet();
ldapTemplate = new LdapTemplate(contextSource);
ldapTemplate.setIgnorePartialResultException(true);
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException
{
initContext(authentication);
Filter filter = new EqualsFilter("sAMAccountName", authentication.getName());
Boolean authenticate = ldapTemplate.authenticate(LdapUtils.emptyLdapName(), filter.encode(), authentication.getCredentials().toString());
if (authenticate)
{
UserDetails userDetails = new User(authentication.getName(), authentication.getCredentials().toString()
, new ArrayList<>());
Authentication auth = new UsernamePasswordAuthenticationToken(userDetails,
authentication.getCredentials().toString(), new ArrayList<>());
return auth;
}
else
{
return null;
}
}
#Override
public boolean supports(Class<?> authentication)
{
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
#Component
public class JwtTokenAuthorizationOncePerRequestFilter extends OncePerRequestFilter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#Autowired
private UserDetailsService JwtLdapUserDetailsService;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Value("${jwt.http.request.header}")
private String tokenHeader;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
logger.debug("Authentication Request For '{}'", request.getRequestURL());
final String requestTokenHeader = request.getHeader(this.tokenHeader);
String username = null;
String jwtToken = null;
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
logger.error("JWT_TOKEN_UNABLE_TO_GET_USERNAME", e);
} catch (ExpiredJwtException e) {
logger.warn("JWT_TOKEN_EXPIRED", e);
}
} else {
logger.warn("JWT_TOKEN_DOES_NOT_START_WITH_BEARER_STRING");
}
logger.debug("JWT_TOKEN_USERNAME_VALUE '{}'", username);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.JwtLdapUserDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
}
Above are some code files (modified for ldap) in the application. Complete code (base of my changes - InMemory based auth+Jwt) is available in the link mentioned above.
Thanks,

spring boot security custom successHandler with rest not working

not sure if my question is good..
Perhaps I was looking very badly for information about the spring security
In general, I hope it will not be difficult for you to answer.
The question is, I use spring security with my login page. The login page is just in the public templates folder. I do not create a separate Controller for it that would return the view page (would it be correct to create a controller for it that would return the view login page?). In any case, my code works even without this page view controller. But only my custom SuccessHandler does not work (which, after login, checks by roles and would redirect to another page).
Should I redirect by role to the appropriate pages using a different approach? (I mean if ADMIN_ROLE after login is redirected to the admin-panel.html)
my security
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class CustomWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserServiceImpl userServiceImpl;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()
.authorizeRequests()
.antMatchers("/", "/templates/sign-up.html").permitAll()
.antMatchers("/api/users", "/api/users/login").permitAll()
.antMatchers("/templates/admin-panel.html").hasRole("ADMIN")
.antMatchers("/all-users").hasRole("ADMIN")
.antMatchers("/news").hasRole("USER")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/templates/login.html")
.defaultSuccessUrl("/")
.permitAll()
.successHandler(myAuthenticationSuccessHandler())
.and()
.logout()
.permitAll()
.logoutSuccessUrl("/index.html");
http.csrf().disable();
}
#Override
public void configure(WebSecurity web) {
web
.ignoring()
.antMatchers("/css/**")
.antMatchers("/js/**")
.antMatchers("/static/**")
.antMatchers("/resources/**");
}
#Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userServiceImpl).passwordEncoder(bCryptPasswordEncoder());
}
#Bean
public AuthenticationSuccessHandler myAuthenticationSuccessHandler(){
return new CustomAuthenticationSuccessHandler();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
my custom success handler
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
protected final Log logger = LogFactory.getLog(this.getClass());
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
public CustomAuthenticationSuccessHandler() {
super();
}
// API
#Override
public void onAuthenticationSuccess(final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication) throws IOException {
handle(request, response, authentication);
clearAuthenticationAttributes(request);
}
// IMPL
protected void handle(final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication) throws IOException {
final String targetUrl = determineTargetUrl(authentication);
if (response.isCommitted()) {
logger.debug("Response has already been committed. Unable to redirect to " + targetUrl);
return;
}
redirectStrategy.sendRedirect(request, response, targetUrl);
}
protected String determineTargetUrl(final Authentication authentication) {
Map<String, String> roleTargetUrlMap = new HashMap<>();
roleTargetUrlMap.put("ROLE_USER", "/index.html");
roleTargetUrlMap.put("ROLE_ADMIN", "/templates/admin-panel.html");
final Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
for (final GrantedAuthority grantedAuthority : authorities) {
String authorityName = grantedAuthority.getAuthority();
if(roleTargetUrlMap.containsKey(authorityName)) {
return roleTargetUrlMap.get(authorityName);
}
}
throw new IllegalStateException();
}
/**
* Removes temporary authentication-related data which may have been stored in the session
* during the authentication process.
*/
protected final void clearAuthenticationAttributes(final HttpServletRequest request) {
final HttpSession session = request.getSession(false);
if (session == null) {
return;
}
session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
}
}
my controller
#CrossOrigin
#RestController
#RequestMapping("/api/users")
public class UserController {
private final UserServiceImpl userService;
private AuthenticationManager authenticationManager;
public UserController(UserServiceImpl userService, AuthenticationManager authenticationManager) {
this.userService = userService;
this.authenticationManager = authenticationManager;
}
#PostMapping
public ResponseEntity<?> register(#RequestBody UserDTO user) {
try {
userService.register(user);
return new ResponseEntity<>("User added", HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(e, HttpStatus.BAD_REQUEST);
}
}
#PostMapping(value = "/login")
public ResponseEntity<?> login(#RequestBody UserDTO user, HttpServletResponse response) {
try {
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()));
boolean isAuthenticated = isAuthenticated(authentication);
if (isAuthenticated) {
SecurityContextHolder.getContext().setAuthentication(authentication);
// response.sendRedirect("/templates/admin-panel.html");
// my pathetic attempt to create a redirect to another page
}
return new ResponseEntity<>("user authenticated", HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(e, HttpStatus.FORBIDDEN);
}
}
private boolean isAuthenticated(Authentication authentication) {
return authentication != null && !(authentication instanceof AnonymousAuthenticationToken) && authentication.isAuthenticated();
}
my static files
enter image description here
My Guess, as you didn't post your login page itself:
You don't need a controller listening to POST /login this normally automatically registered by Spring Security with all security related authentication stuff. No need to try it by yourself as in UserController.login(). I guess by regsitering this endpoint you override / deactivate the regular spring security behaviour.
Normally you just need a login page with a form that posts correctly to /login. The handling on the backend side is done by spring security itself.
See https://spring.io/guides/gs/securing-web/ for a minimal worling setup.

why spring security hasRole function does not authenticate any apis

I have some trouble to make it work with spring security hasRole. I have 2 Role in db saved as ROLE_ADMIN and ROLE_USER. I want to give permisson some APIs with ADMIN role, some with USER role. HERE is my code.
SecurityConfig
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Value("${spring.queries.users-query}")
private String usersQuery;
#Value("${spring.queries.roles-query}")
private String rolesQuery;
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.dataSource(dataSource)
.passwordEncoder(bCryptPasswordEncoder())
.usersByUsernameQuery(usersQuery)
.authoritiesByUsernameQuery(rolesQuery);
}
#Override
protected void configure(HttpSecurity http) {
System.out.println("configure " );
try {
http.csrf().disable().authorizeRequests()
.antMatchers("/", "/*.html").permitAll()
.antMatchers("/home").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/profile/").hasAnyRole("ADMIN","USER")
.antMatchers("/admin/*").hasRole("ADMIN")
.antMatchers("/insurance/*").hasRole("ADMIN")
.antMatchers("/company/*").hasRole("ADMIN")
.anyRequest().authenticated();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void configure(WebSecurity web) {
web.httpFirewall(allowUrlEncodedSlashHttpFirewall())
.ignoring()
.antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**", "/templates/**");
}
#Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
/*firewall.setAllowUrlEncodedSlash(true);
firewall.setAllowSemicolon(true);*/
firewall.setAllowUrlEncodedDoubleSlash(true);
return firewall;
}
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
And I have sql queries in application.properties
spring.queries.users-query=select username, password, status from insurance.users where username=?
spring.queries.roles-query=select u.username, r.role from insurance.users u inner join insurance.roles r on(u.role_id=r.id) where u.username=?
Problem is that when I try to login, I get 403 error code.
Here is Controller.class
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(#RequestParam(value = "email") String email,
#RequestParam(value = "password") String password, HttpSession session) {
Result result = userService.login(email, password);
session.setAttribute("user", result.getData());
if(result.getStatus() == 200){
return "redirect:/profile";
} else {
return "redirect:/login?error";
}
}
#RequestMapping(value = "/profile", method = RequestMethod.GET)
public String profile(HttpSession httpSession, Model model) {
if(httpSession.getAttribute("user") != null) {
UserResponse user = (UserResponse) httpSession.getAttribute("user");
model.addAttribute("user", user);
return "profile";
} else {
return "redirect:/home";
}
}
I have tried to solve it, but could not find. If you have any advice, please tell.
I changed my config file as suggested. I added my custom login logic, now when I want to go /admins or another url, I redirect to login url Here is my config code
protected void configure(HttpSecurity http) {
try {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/home").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/insurance/*").hasRole("ADMIN")
.antMatchers("/company/*").hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/login.html").permitAll().usernameParameter("username") .passwordParameter("password")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/profile.html", true);
} catch (Exception e) {
e.printStackTrace();
}
}
Just add login also in exceptions for authentication.
Give this a try
protected void configure(HttpSecurity http) {
try {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/home").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/insurance/*").hasRole("ADMIN")
.antMatchers("/company/*").hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/login.html").permitAll().usernameParameter("username") .passwordParameter("password")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/profile.html", true);
} catch (Exception e) {
e.printStackTrace();
}
}

Can i use two different tables for login in my spring boot application by spring security?

In my current project I have two separate entities.
User :- TO authenticate users
Customer :- To authenticate customers
I'm confuse How will we manage login process in same project for two separate entities by spring security?
As of now its working with one User entity , now i have to integrate one more login for customers with the help of Customer table.
is it possible ?
Note :- Due to some another requirement i can't use the same table for both users and customer.
I am sharing some code for more clearance.
Below code is the implementation of user detail service.
private final Logger log = LoggerFactory.getLogger(DomainUserDetailsService.class);
private final UserLoginRepository userRepository;
public DomainUserDetailsService(UserLoginRepository userRepository) {
this.userRepository = userRepository;
}
#Override
#Transactional
public UserDetails loadUserByUsername(final String login) {
log.debug("Authenticating {}", login);
if (new EmailValidator().isValid(login, null)) {
Optional<User> userByEmailFromDatabase = userRepository.findOneWithAuthoritiesByLogin(login);
return userByEmailFromDatabase.map(user -> createSpringSecurityUser(login, user))
.orElseThrow(() -> new UsernameNotFoundException("User with email " + login + " was not found in the database"));
}
String lowercaseLogin = login.toLowerCase(Locale.ENGLISH);
Optional<User> userByLoginFromDatabase = userRepository.findOneWithAuthoritiesByLogin(lowercaseLogin);
return userByLoginFromDatabase.map(user -> createSpringSecurityUser(lowercaseLogin, user))
.orElseThrow(() -> new UsernameNotFoundException("User " + lowercaseLogin + " was not found in the database"));
}
private org.springframework.security.core.userdetails.User createSpringSecurityUser(String lowercaseLogin, User user) {
List<GrantedAuthority> grantedAuthorities = user.getAuthorities().stream()
.map(authority -> new SimpleGrantedAuthority(authority.getName()))
.collect(Collectors.toList());
return new org.springframework.security.core.userdetails.User(user.getLogin(),
user.getPassword(),
grantedAuthorities);
}
}
Below is the implantation of security config class.
private final AuthenticationManagerBuilder authenticationManagerBuilder;
private final UserDetailsService userDetailsService;
private final TokenProvider tokenProvider;
private final CorsFilter corsFilter;
private final SecurityProblemSupport problemSupport;
public SecurityConfiguration(AuthenticationManagerBuilder authenticationManagerBuilder, UserDetailsService userDetailsService,TokenProvider tokenProvider,CorsFilter corsFilter, SecurityProblemSupport problemSupport) {
this.authenticationManagerBuilder = authenticationManagerBuilder;
this.userDetailsService = userDetailsService;
this.tokenProvider = tokenProvider;
this.corsFilter = corsFilter;
this.problemSupport = problemSupport;
}
#PostConstruct
public void init() {
try {
authenticationManagerBuilder
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
} catch (Exception e) {
throw new BeanInitializationException("Security configuration failed", e);
}
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/app/**/*.{js,html}")
.antMatchers("/i18n/**")
.antMatchers("/content/**")
.antMatchers("/swagger-ui/index.html");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(problemSupport)
.accessDeniedHandler(problemSupport)
.and()
.csrf()
.disable()
.headers()
.frameOptions()
.disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/register").permitAll()
.antMatchers("/api/activate").permitAll()
.antMatchers("/api/userLogin").permitAll()
.antMatchers("/api/account/reset-password/init").permitAll()
.antMatchers("/api/account/reset-password/finish").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/info").permitAll()
.antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/v2/api-docs/**").permitAll()
.antMatchers("/swagger-resources/configuration/ui").permitAll()
.antMatchers("/swagger-ui/index.html").hasAuthority(AuthoritiesConstants.ADMIN)
.and()
.apply(securityConfigurerAdapter());
}
private JWTConfigurer securityConfigurerAdapter() {
return new JWTConfigurer(tokenProvider);
}
}
Login api
#PostMapping("/userLogin")
#Timed
public Response<JWTToken> authorize(
#Valid #RequestBody UserLoginReq userLoginReq) {
Map<String, Object> responseData = null;
try {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
userLoginReq.getUsername(), userLoginReq.getPassword());
Authentication authentication = this.authenticationManager
.authenticate(authenticationToken);
SecurityContextHolder.getContext()
.setAuthentication(authentication);
}
Yes you can pass user type combined in userName, separated by a character like :
Example:
String userName=inputUserName +":APP_USER";
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(userName, password);
in UserDetailsService.loadUserByUsername(userName)
first split get the userName part and also get userType part.
Now on userType base you can decide the user should be authenticate from which table.
String userNamePart = null;
if (userName.contains(":")) {
int colonIndex = userName.lastIndexOf(":");
userNamePart = userName.substring(0, colonIndex);
}
userNamePart = userNamePart == null ? userName : userNamePart;
String userTypePart = null;
if (userName.contains(":")) {
int colonIndex = userName.lastIndexOf(":");
userTypePart = userName.substring(colonIndex + 1, userName.length());
}
At first customer is also user, isn't it? So maybe simpler solution would be to create customer also as user (use some flag/db field usertype ={USER|CUSTOMER|...}). If you still need to manage two entities, your approach is right, but In your DetailService just implement the another method which will read customer entity and then create spring's User.
I faced the same problem too!
It gave a error message like following:
AuthenticationManager This predefined class will help you to achieve this.
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
To over come this issue we should use Qualifier!
Please go through the following link , it will guide you to use qualifier
This is my first answer give it a like!
https://developpaper.com/can-spring-security-dock-multiple-user-tables-at-the-same-time/
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
#Primary
UserDetailsService us1() {
return new InMemoryUserDetailsManager(User.builder().username("javaboy").password("{noop}123").roles("admin").build());
}
#Bean
UserDetailsService us2() {
return new InMemoryUserDetailsManager(User.builder().username("sang").password("{noop}123").roles("user").build());
}
#Override
#Bean
protected AuthenticationManager authenticationManager() throws Exception {
DaoAuthenticationProvider dao1 = new DaoAuthenticationProvider();
dao1.setUserDetailsService(us1());
DaoAuthenticationProvider dao2 = new DaoAuthenticationProvider();
dao2.setUserDetailsService(us2());
ProviderManager manager = new ProviderManager(dao1, dao2);
return manager;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/hello").hasRole("user")
.antMatchers("/admin").hasRole("admin")
.and()
.formLogin()
.loginProcessingUrl("/doLogin")
.permitAll()
.and()
.csrf().disable();
}
}

How to change intercept url in Spring Security without having to re-deploy the application?

I'm currently trying to build a database UI implementing spring security, but i'm stuck on how to change the intercept url access from access=hasRole('ROLE_ADMIN') to access=denyAll and deny any user from accessing that particular page without having me to logout.
this is my WebSecurityConfig class:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Value("${users-by-username-query}")
private String usersQuery;
#Value("${authorities-by-username-query}")
private String authoritiesQuery;
#Autowired
private MyAuthenticationHandler myAuthenticationHandler;
private CustomAccessDecisionManager customAccessDecisionManager;
#Autowired
private Service service;
#Override
protected void configure(HttpSecurity http) throws Exception {
List<UrlRole> viewPermissions = service.findAllUrlRole();
System.out.println("Return from service class with size "+viewPermissions.size());
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry interceptUrlRegistry = http
// .authorizeRequests().antMatchers("/","/hello.html","/footer.jsp","/header.jsp","/sidebar.jsp","/reg_issuer.jsp","/reg_user.jsp","/rest/**","/IssuerList.jsp","/loginSecurity","/index.jsp","/verify_otp.jsp")
.authorizeRequests().antMatchers("/","/rest/**")
.permitAll();
for (int i = 0;i<viewPermissions.size();i++) {
String url = viewPermissions.get(i).getUrl();
String string = "";
if(viewPermissions.get(i).getRole().equalsIgnoreCase("denyAll")){
string = viewPermissions.get(i).getRole();
}else{
string = "hasRole('"+viewPermissions.get(i).getRole()+"')";
for (int j = 0;j<viewPermissions.size();j++) {
if(j!=i && viewPermissions.get(j).getUrl().equalsIgnoreCase(url) ){
string+=" or hasRole('"+viewPermissions.get(j).getRole()+"')";
}
}
}
interceptUrlRegistry.antMatchers(viewPermissions.get(i).getUrl()).access(string);
}
interceptUrlRegistry.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").successHandler(myAuthenticationHandler)
.usernameParameter("username")
.passwordParameter("password")
.permitAll()
.and()
.logout()
.permitAll()
.and()
.exceptionHandling()
.accessDeniedPage("/403");
}
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery(usersQuery)
.authoritiesByUsernameQuery(authoritiesQuery);
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**", "/assets/**");
}
}
Currently i am looping the "antMatchers(viewPermissions.get(i).getUrl()).access(string)" to get the url and roles from database but it only been done when the first time i deploy it in wildfly. That is why the new access for url will not be implemented unless i restart the wildfly server.
Is there anyway to implement it without having to restart the server?
THE ANSWERED I GOT AND WORKS FOR ME IS AS BELOW.
this is my new WebSecurityConfig class:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Value("${users-by-username-query}")
private String usersQuery;
#Value("${authorities-by-username-query}")
private String authoritiesQuery;
#Autowired
private MyAuthenticationHandler myAuthenticationHandler;
#Autowired
private Service service;
#Override
protected void configure(HttpSecurity http) throws Exception {
List<UrlRole> viewPermissions = service.findAllUrlRole();
System.out.println("Return from service class with size "+viewPermissions.size());
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry interceptUrlRegistry = http
.authorizeRequests().antMatchers("/rest/**")
.permitAll();
interceptUrlRegistry.antMatchers("/login").access("hasRole('ROLE_ANONYMOUS')");
interceptUrlRegistry.anyRequest().authenticated().accessDecisionManager(accessDecisionManager())
.and()
.formLogin()
.loginPage("/login").successHandler(myAuthenticationHandler)
.usernameParameter("username")
.passwordParameter("password")
.and()
.logout()
.permitAll()
.and()
.exceptionHandling()
.accessDeniedPage("/403");
}
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery(usersQuery)
.authoritiesByUsernameQuery(authoritiesQuery);
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**", "/assets/**","/rest/findAllUrlRole","/error","/403","/404","/500");
}
#SuppressWarnings("unchecked")
#Bean
public AccessDecisionManager accessDecisionManager() {
System.out.println("Arrive AccessDecisionManager");
List<AccessDecisionVoter<? extends Object>> decisionVoters
= Arrays.asList(
new WebExpressionVoter(),
new RoleVoter(),
new AuthenticatedVoter(),
new MinuteBasedVoter());
System.out.println("End of AccessDecisionManager: "+ decisionVoters);
return new UnanimousBased(decisionVoters);
}
}
this is my MinuteBasedVoter class:
#SuppressWarnings("rawtypes")
public class MinuteBasedVoter implements AccessDecisionVoter {
#Override
public int vote(
Authentication authentication, Object object, Collection collection) {
WebServiceTester a = new WebServiceTester();
String username = authentication.getName(); //to get current user role
String url = ((FilterInvocation) object).getRequestUrl(); // to get current url
boolean NONanonymous = true;
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
for (GrantedAuthority grantedAuthority : authorities) {
if(grantedAuthority.getAuthority().equalsIgnoreCase("ROLE_ANONYMOUS")){
NONanonymous = false;
}
}
int vote = ACCESS_ABSTAIN;
boolean NONexist = true;
if(NONanonymous){
List<Role> roles = new ArrayList<Role>();
Role role = new Role();
vote = ACCESS_DENIED;
try{
List<UrlRole> urlroles = a.findAllUrlRole(); // to get all url and its respective role
// below is how i match the role of current user and the role that can access the current url
for(int i = 0; i<urlroles.size();i++){
if(url.startsWith(urlroles.get(i).getUrl())){
NONexist = false;
System.out.println("URL: "+url+" , Role: "+urlroles.get(i).getRole());
role.setRole(urlroles.get(i).getRole());
roles.add(role);
for (GrantedAuthority grantedAuthority : authorities) {
if(grantedAuthority.getAuthority().equalsIgnoreCase(urlroles.get(i).getRole())){
vote = ACCESS_GRANTED;
}
}
}
}
}catch(Exception e){
System.out.println("Error at MinuteBasedVoter: "+e);
}
if(NONexist){
vote = ACCESS_GRANTED;
}
}
return vote;
}
#Override
public boolean supports(ConfigAttribute attribute) {
// TODO Auto-generated method stub
return true;
}
#Override
public boolean supports(Class clazz) {
// TODO Auto-generated method stub
return true;
}
}
i got this solution from http://www.baeldung.com/spring-security-custom-voter but with a twist of my own.

Resources