why spring security hasRole function does not authenticate any apis - spring

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();
}
}

Related

User Authorization with custom roles

My database is a table with Users with {Username, Password, Role}. This role can either be Administrator or student.
I want my security Config file to permit some pages for students while others are only for administrators. For now this SecurityConfig looks like this...
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**")
.permitAll()
.antMatchers("/api/reservation").hasRole("administrator")
.antMatchers("/api/reservation/**").hasRole("student")
.antMatchers("/api/rooms/**")
.permitAll()
.antMatchers("/v2/api-docs",
"/configuration/ui",
"/swagger-resources/**",
"/configuration/security",
"/swagger-ui.html",
"/webjars/**")
.permitAll()
.anyRequest()
.authenticated();
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
I think you also need my UserDetailsImplementation in order to help:
#Service
#AllArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserRepo userRepository;
#Override
#Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) {
Optional<User> userOptional = userRepository.findByUsername(username);
User user = userOptional.orElseThrow(() -> new UsernameNotFoundException("No user " + "Found with username : " + username));
String rol = "";
if (user.getRole().equals("administrator")) {
rol = "ADMIN";
}
else if (user.getRole().equals("student")) {
rol = "STUDENT";
}
return new org.springframework.security
.core.userdetails.User(user.getUsername(), user.getPassword(),
user.isEnabled(), true, true,
true, getAuthorities(rol));
}
private Collection<? extends GrantedAuthority> getAuthorities(String role) {
return singletonList(new SimpleGrantedAuthority(role));
}
}
I already tried the SecurityConfig with .hasRole("student"), etc. But it didn't work.
I found this old project of mine that worked with a inMemory DB and used this:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// users worden hier gedefined
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
auth
.inMemoryAuthentication()
.withUser("user").password(encoder.encode("t")).roles("USER")
.and()
.withUser("admin").password(encoder.encode("t")).roles("ADMIN", "USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// welke httprequests afgeschermd moeten worden
http
.authorizeRequests()
// hier patronen definieren die moeten matchen
.antMatchers("/").hasRole("USER")
.antMatchers("/maincontroller/add-bus").hasRole("ADMIN")
.antMatchers("/maincontroller/add-team").hasRole("ADMIN")
.antMatchers("/maincontroller/add-speler").hasRole("ADMIN")
.antMatchers("/maincontroller/add-wedstrijd").hasRole("ADMIN")
.antMatchers("/maincontroller/update-bus/*").hasRole("ADMIN")
.antMatchers("/maincontroller/update-team/*").hasRole("ADMIN")
.antMatchers("/maincontroller/update-speler/*").hasRole("ADMIN")
.antMatchers("/maincontroller/update-wedstrijd/*").hasRole("ADMIN")
.antMatchers("/maincontroller/delete-bus/*").hasRole("ADMIN")
.antMatchers("/maincontroller/delete-team/*").hasRole("ADMIN")
.antMatchers("/maincontroller/delete-speler/*").hasRole("ADMIN")
.antMatchers("/maincontroller/delete-wedstrijd/*").hasRole("ADMIN")
.antMatchers("/maincontroller/*").hasRole("USER")
.antMatchers("/api/**").permitAll()
So to summarize my problem: I would like to link the roles in my database with the hasRole() function in my securityConfig.
Thanks in advance!

Spring Boot with Spring Security - Two Factor Authentication with SMS/ PIN/ TOTP

I'm working on a Spring Boot 2.5.0 web application with Spring Security form login using Thymeleaf. I'm looking for ideas on how to implement two factor authentication (2FA) with spring security form login.
The requirement is that when a user logs in with his username and password via. the login form, if the username and password authentication is successful an SMS code should be sent to the registered mobile number of the user and he should be challenged with another page to enter the SMS code. If user gets the SMS code correctly, he should be forwarded to the secured application page.
On the login form, along with the username and password, the user is also requested to enter the text from a captcha image which is verified using a SimpleAuthenticationFilter which extends UsernamePasswordAuthenticationFilter.
This is the current SecurityConfiguration
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsServiceImpl userDetailsService;
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers(
"/favicon.ico",
"/webjars/**",
"/images/**",
"/css/**",
"/js/**",
"/login/**",
"/captcha/**",
"/public/**",
"/user/**").permitAll()
.anyRequest().authenticated()
.and().formLogin()
.loginPage("/login")
.permitAll()
.defaultSuccessUrl("/", true)
.and().logout()
.invalidateHttpSession(true)
.clearAuthentication(true)
.deleteCookies("JSESSONID")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login?logout")
.permitAll()
.and().headers().frameOptions().sameOrigin()
.and().sessionManagement()
.maximumSessions(5)
.sessionRegistry(sessionRegistry())
.expiredUrl("/login?error=5");
}
public SimpleAuthenticationFilter authenticationFilter() throws Exception {
SimpleAuthenticationFilter filter = new SimpleAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
filter.setAuthenticationFailureHandler(authenticationFailureHandler());
return filter;
}
#Bean
public AuthenticationFailureHandler authenticationFailureHandler() {
return new CustomAuthenticationFailureHandler();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
auth.setUserDetailsService(userDetailsService);
auth.setPasswordEncoder(passwordEncoder());
return auth;
}
/** TO-GET-SESSIONS-STORED-ON-SERVER */
#Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
}
And this is the SimpleAuthenticationFilter mentioned above.
public class SimpleAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public static final String SPRING_SECURITY_FORM_CAPTCHA_KEY = "captcha";
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
HttpSession session = request.getSession(true);
String captchaFromSession = null;
if (session.getAttribute("captcha") != null) {
captchaFromSession = session.getAttribute("captcha").toString();
} else {
throw new CredentialsExpiredException("INVALID SESSION");
}
String captchaFromRequest = obtainCaptcha(request);
if (captchaFromRequest == null) {
throw new AuthenticationCredentialsNotFoundException("INVALID CAPTCHA");
}
if (!captchaFromRequest.equals(captchaFromSession)) {
throw new AuthenticationCredentialsNotFoundException("INVALID CAPTCHA");
}
UsernamePasswordAuthenticationToken authRequest = getAuthRequest(request);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
private UsernamePasswordAuthenticationToken getAuthRequest(HttpServletRequest request) {
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
return new UsernamePasswordAuthenticationToken(username, password);
}
private String obtainCaptcha(HttpServletRequest request) {
return request.getParameter(SPRING_SECURITY_FORM_CAPTCHA_KEY);
}
}
Any ideas on how to approach this ? Thanks in advance.
Spring Security has an mfa sample to get you started. It uses Google Authenticator with an OTP, but you can plug in sending/verifying your SMS short-code instead.
You might also consider keeping the captcha verification separate from the (out of the box) authentication filter. If they are separate filters in the same filter chain, it will have the same effect with less code.

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.

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.

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;
}

Resources