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

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.

Related

Spring security: My Authorization filter authorizes my request even tho the URL is permited

In my security configuration class i have permitted the request to the welcome url and any other url which follows the "welcome/**" format.
this is my securityconfiguration class:
#EnableGlobalMethodSecurity(prePostEnabled = true)
//#Configuration
#EnableWebSecurity
public class JwtSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
private final CustomerDetailsService customerDetailsService;
#Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
#Autowired
public JwtSecurityConfiguration(CustomerDetailsService customerDetailsService) {
this.customerDetailsService = customerDetailsService;
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(customerDetailsService)
.passwordEncoder(passwordEncoderBean());
}
#Bean
public PasswordEncoder passwordEncoderBean() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("**/resources/static/**")
.and()
.ignoring()
.antMatchers(
HttpMethod.GET,
"/",
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/index_assets/**"
);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/welcome/login").permitAll()
.antMatchers("/welcome").permitAll()
.antMatchers("/welcome/signup").permitAll()
.antMatchers("admin/rest/**").authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
//http.addFilterBefore(new JWTAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(new JWTAuthorizationFilter(authenticationManager(),customerDetailsService),UsernamePasswordAuthenticationFilter.class);
// disable page caching
http
.headers()
.frameOptions().sameOrigin() // required to set for H2 else H2 Console will be blank.
.cacheControl();
//http.headers().cacheControl();
}
}
but I noticed that in my JWTAuthorizationFilter.class the doFilterInternal() method picks up this URL
public class JWTAuthorizationFilter extends OncePerRequestFilter {
private final CustomerDetailsService customerDetailsService;
#Autowired
DefaultCookieService defaultCookieService;
public JWTAuthorizationFilter(AuthenticationManager authenticationManager, CustomerDetailsService customerDetailsService) {
// super(authenticationManager);
this.customerDetailsService = customerDetailsService;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String header = request.getHeader(HEADER);
if(Objects.isNull(header) || !header.startsWith(TOKEN_PREFIX)){
return;
}
UsernamePasswordAuthenticationToken usernamePasswordAuth = getAuthenticationToken(request);
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuth);
chain.doFilter(request,response);
}
private UsernamePasswordAuthenticationToken getAuthenticationToken(HttpServletRequest request){
String token = request.getHeader(HEADER);
if(Objects.isNull(token)) return null;
String username = Jwts.parser().setSigningKey(SECRET)
.parseClaimsJws(token.replace(TOKEN_PREFIX,""))
.getBody()
.getSubject();
UserDetails userDetails = customerDetailsService.loadUserByUsername(username);
return username != null ? new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()) : null;
}
}
What is the cause of this ?
Filter is suppose to pick up each and every request. It doesn't matter if that you have permitted or not in security configuration.
You have got two options:
If you don't want welcome/** to go through the filter you add it to web ignore
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("**/resources/static/**")
.and()
.ignoring()
.antMatchers(
HttpMethod.GET,
"/",
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/index_assets/**",
"/welcome/**"
);
}
But note, it will skip all filters and you may not want that.
In doFilterInternal method skip it when you find welcome/** pattern.

Spring security - Custom authentication provided not called

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.

Spring security using JWT, how to exclude certain endpoints like /login from being authenticated using JwtAuthenticationFilter?

I want to exclude /login url from being authenticated by spring security.
My configuration class looks like'
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().authorizeRequests().antMatchers("/v1/pricing/login").permitAll()
.antMatchers("v1/pricing/**").authenticated().and()
.addFilterBefore(corsFilter,UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v1/pricing/login");
}
JwtAuthenticationFilter looks like
- commented the exception part, as it starts throwing exception in login also
#Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
#Autowired
JwtTokenProvider jwtTokenProvider;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && jwtTokenProvider.validateToken(jwt)) {
String[] userInfo = jwtTokenProvider.getUserDetailsFromJWT(jwt);
UserDetails userDetails = new UserPrincipal(Long.parseLong(userInfo[0]), userInfo[1], userInfo[2], null,
userInfo[3]);
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(userDetails, null, null);
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String token = request.getHeader("Authorization");
if (StringUtils.hasText(token)) {
return token;
} /*else {
throw new AuthenticationServiceException("Authorization header cannot be blank!");
}*/
return null;
}
}
Any request with /v1/pricing/login still goes to JWtAuthentication filter and fails.
JwtTokenAuthenticationProcessingFilter filter is configured to skip following endpoints: /api/auth/login and /api/auth/token. This is achieved with SkipPathRequestMatcher implementation of RequestMatcher.
public class SkipPathRequestMatcher implements RequestMatcher {
private OrRequestMatcher matchers;
private RequestMatcher processingMatcher;
public SkipPathRequestMatcher(List<String> pathsToSkip, String processingPath) {
Assert.notNull(pathsToSkip);
List<RequestMatcher> m = pathsToSkip.stream().map(path -> new AntPathRequestMatcher(path)).collect(Collectors.toList());
matchers = new OrRequestMatcher(m);
processingMatcher = new AntPathRequestMatcher(processingPath);
}
#Override
public boolean matches(HttpServletRequest request) {
if (matchers.matches(request)) {
return false;
}
return processingMatcher.matches(request) ? true : false;
}
}
Then call :
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
public static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization";
public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login";
public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**";
public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token";
protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() throws Exception {
List<String> pathsToSkip = Arrays.asList(TOKEN_REFRESH_ENTRY_POINT, FORM_BASED_LOGIN_ENTRY_POINT);
SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT);
JwtTokenAuthenticationProcessingFilter filter
= new JwtTokenAuthenticationProcessingFilter(failureHandler, tokenExtractor, matcher);
filter.setAuthenticationManager(this.authenticationManager);
return filter;
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(ajaxAuthenticationProvider);
auth.authenticationProvider(jwtAuthenticationProvider);
}
#Bean
protected BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable() // We don't need CSRF for JWT based authentication
.exceptionHandling()
.authenticationEntryPoint(this.authenticationEntryPoint)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(FORM_BASED_LOGIN_ENTRY_POINT).permitAll() // Login end-point
.antMatchers(TOKEN_REFRESH_ENTRY_POINT).permitAll() // Token refresh end-point
.antMatchers("/console").permitAll() // H2 Console Dash-board - only for testing
.and()
.authorizeRequests()
.antMatchers(TOKEN_BASED_AUTH_ENTRY_POINT).authenticated() // Protected API End-points
.and()
.addFilterBefore(buildAjaxLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(buildJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
}
}

spring security - role based access

I have implemented spring security for my webapp.
I want to configure role based access. Only users with the role "ROLE_ADMIN" should be abeĺe to login.
I added the model "Role" and added a table in my database.
However users with the role "ROLE_USER" are still able to login.
#Override
protected void configure(HttpSecurity http) {
try {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/resources/**").hasRole("ROLE_ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
} catch (Exception e) {
e.printStackTrace();
}
}
Thanks!
Edit: complete spring security config
#Configuration
#EnableWebSecurity
#ComponentScan(basePackageClasses = UserDetailsServiceImpl.class)
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/css/**", "/js/**");
}
#Override
protected void configure(HttpSecurity http) {
try {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/resources/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
} catch (Exception e) {
e.printStackTrace();
}
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(bCryptPasswordEncoder());
return authProvider;
}
#Autowired
public void globalSecurityConfiguration(AuthenticationManagerBuilder auth) {
try {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
} catch (Exception e) {
e.printStackTrace();
}
}
}
Are you extending WebMvcConfigurerAdapter ? Also hasRole will prefix the provided string with "ROLE_"
from doc:
the role to require (i.e. USER, ADMIN, etc). Note, it should not start with "ROLE_" as this is automatically inserted.
example:
#SpringBootApplication
public class SampleWebSecureJdbcApplication extends WebMvcConfigurerAdapter {
public static void main(String[] args) throws Exception {
new SpringApplicationBuilder(SampleWebSecureJdbcApplication.class).run(args);
}
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**", "/signup", "/about").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").failureUrl("/login?error").permitAll()
.and()
.logout().permitAll();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(this.dataSource);
}
}
}
I have implemented a Role based access where after the login admin user will be directed to the admin homepage and normal user will be redirected to the user homepage.
Below is my SecurityConfiguration class.
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
private DataSource dataSource;
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
final String sqlUserName = "select email, password, active from user where email=?";
final String sqlAuthorities= "select u.email, r.role from user u inner join user_role ur on(u.user_id=ur.user_id) inner join role r on(ur.role_id=r.role_id) where u.email=?";
auth.
jdbcAuthentication()
.usersByUsernameQuery(sqlUserName)
.authoritiesByUsernameQuery(sqlAuthorities)
.dataSource(dataSource)
.passwordEncoder(bCryptPasswordEncoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http. authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/registration").permitAll()
.antMatchers("/resources/**", "/static/**", "/static.css/**", "/js/**", "/static.images/**").permitAll()
.antMatchers("/user").hasAuthority("USER")
.antMatchers("/home").hasAuthority("ADMIN").anyRequest()
.authenticated().and().csrf().disable().formLogin()
.loginPage("/login").failureUrl("/login?error=true")
.defaultSuccessUrl("/loginroute",true)
.usernameParameter("email")
.passwordParameter("password")
.and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/").and().exceptionHandling()
.accessDeniedPage("/access-denied");
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**", "/static/**", "/static.css/**", "/js/**", "/static.images/**");
}
}
.defaultSuccessUrl("/loginroute",true) will redirect to the /loginroute controller. Below is the controller methods.
#RequestMapping (value = "/loginroute",method = RequestMethod.GET)
public String sample(){
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
User user = userService.findUserByEmail(auth.getName());
String rolevalue = null;
for (Role role : user.getRoles()) {
rolevalue = role.getRole();
}
System.out.println(user.getRoles().contains("role"));
if(rolevalue.equals("ADMIN"))
return "redirect:home";
else if(rolevalue.equals("USER"))
return "redirect:user";
return "User does not have permission";
}
#RequestMapping(value="/home", method = RequestMethod.GET)
public ModelAndView home(){
ModelAndView modelAndView = new ModelAndView();
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
User user = userService.findUserByEmail(auth.getName());
modelAndView.addObject("userName", "Welcome " + user.getName() + " " + user.getLastName() + " (" + user.getEmail() + ")");
modelAndView.addObject("adminMessage","Content Available Only for Users with Admin Role");
modelAndView.setViewName("home");
return modelAndView;
}
#RequestMapping(value="/user", method = RequestMethod.GET)
public ModelAndView userhome(){
ModelAndView modelAndView = new ModelAndView();
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
User user = userService.findUserByEmail(auth.getName());
modelAndView.addObject("userName", "Welcome user: " + user.getName() + " " + user.getLastName() + " (" + user.getEmail() + ")");
modelAndView.addObject("userMessage","Content Available Only for Users with User Role");
modelAndView.setViewName("user");
return modelAndView;
}

Spring injection of userdetailsservice in security config class fails

I am new to spring security and i am trying to configure it using java but when i try to inject a UserDetailsService into a security config class i get a 404 error page but when i inject it into a controller the injection works. am using spring version 4.1.6 and spring security 4.0.0
here is my security config class
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("loginService")
UserDetailsService loginService; //THIS IS THE POINT OF FAILURE
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/")
.usernameParameter("username")
.passwordParameter("password")
.defaultSuccessUrl("/userlist")
.failureUrl("/")
.permitAll()
.and()
.logout()
.permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
/*auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");*/
auth.userDetailsService(loginService).passwordEncoder(passwordEncoder());
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**");
}
#Bean
public Md5PasswordEncoder passwordEncoder(){
Md5PasswordEncoder encoder = new Md5PasswordEncoder();
return encoder;
}
}
This is the UserDetailsService class
#Service("loginService")
public class LoginService implements UserDetailsService{
#Autowired
UserRepository userRepository;
#Transactional
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SiteUser user = userRepository.findByUsername(username);
Collection<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
SimpleGrantedAuthority userAuthority = new SimpleGrantedAuthority("ROLE_USER");
SimpleGrantedAuthority adminAuthority = new SimpleGrantedAuthority("ROLE_ADMIN");
User u = null;
if(user == null)
throw new UsernameNotFoundException("No such User: " + username);
else
{
if (user.getRole().equals("USER"))
authorities.add(userAuthority);
else if (user.getRole().equals("ADMIN"))
{
authorities.add(userAuthority);
authorities.add(adminAuthority);
}
u = new User(user.getUsername(), user.getPassword(), authorities);
}
return u;
}
}
The rest of the project is available
here
The solution was to add
#ComponentScan("com.ashken.*")
on top of the securityconfig class
I've found that it is less troublesome to simply register your implementation of UserDetailsService as a bean in SecurityConfig:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserRepository userRepository;
#Bean
public UserDetailsService userDetailsService() {
return new UserDetailsService() {
#Transactional
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SiteUser user = userRepository.findByUsername(username);
Collection<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
SimpleGrantedAuthority userAuthority = new SimpleGrantedAuthority("ROLE_USER");
SimpleGrantedAuthority adminAuthority = new SimpleGrantedAuthority("ROLE_ADMIN");
User u = null;
if(user == null) {
throw new UsernameNotFoundException("No such User: " + username);
} else {
if (user.getRole().equals("USER")) {
authorities.add(userAuthority);
} else if (user.getRole().equals("ADMIN")) {
authorities.add(userAuthority);
authorities.add(adminAuthority);
}
u = new User(user.getUsername(), user.getPassword(), authorities);
}
return u;
}
};
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/")
.usernameParameter("username")
.passwordParameter("password")
.defaultSuccessUrl("/userlist")
.failureUrl("/")
.permitAll()
.and()
.logout()
.permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
/*auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");*/
auth.userDetailsService(loginService).passwordEncoder(passwordEncoder());
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**");
}
#Bean
public Md5PasswordEncoder passwordEncoder(){
Md5PasswordEncoder encoder = new Md5PasswordEncoder();
return encoder;
}
}

Resources