Spring Boot / Spring Security role based authorization not working properly [duplicate] - spring

This question already has answers here:
Springboot Security hasRole not working
(3 answers)
Closed 1 year ago.
I am trying to learn Spring Security with Spring Boot. I have a demo where I am implementing it. It works fine with login page and profile method which can be authenticate by any valid user. But when I am trying to access for a specific role then it does not work and gives me a "403 - access denied".
My access point >>
#Controller
public class HomeController {
#RequestMapping("/")
public String home() {
return "/home.jsp";
}
#RequestMapping("/profile")
public String profile() {
return "/profile.jsp";
}
#RequestMapping("/admin")
public String admin() {
return "/admin.jsp";
}
#RequestMapping("/management")
public String management() {
return "/management.jsp";
}
}
My configure method >>
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/login", "/").permitAll()
.antMatchers("/profile").authenticated()
.antMatchers("/admin").hasRole("ADMIN")
.antMatchers("/management").hasAnyRole("ADMIN", "MANAGEMENT")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").permitAll()
.and()
.logout().invalidateHttpSession(true)
.clearAuthentication(true)
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/logout-success").permitAll();
}
My role assigned >>
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.singleton(new SimpleGrantedAuthority("ADMIN"));
}

I think that's the most unobvious thing about Spring Security. Roles and authorities are the same things but roles should be prefixed with ROLE_. So, the correct usage is
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.singleton(new SimpleGrantedAuthority("ROLE_ADMIN"));
}

Related

Spring Security not letting unauthorized users reach the login page

I am using Spring Security and trying to add a custom login form, the browser does get redirected to the correct URL but I get a message along the lines of
The page isn’t redirecting properly
and can't see the login page at all.
Under the network tab (when I press F12) I see multiple requests to my custom login page, so I'm guessing Spring sees I'm unauthorized then redirects me to the login page over and over effectively creating a loop.
This is the code for my security configuration:
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/showMyLoginPage")
.permitAll();
return http.build();
}
Tried removing the loginPage() bit, solving the issue but yielding the default login page.
requested image
Request and response show no info..
Controller I'm using
#Controller
public class MainController {
#RequestMapping("/")
public String testMapping()
{
return "home";
}
#RequestMapping("/showMyLoginPage")
public String loginPage()
{
return "users-login";
}
}
My debug log: https://pastebin.com/LagTN71L
My configuration classes: (won't show hibernate or c3p0)
#Configuration
#EnableWebMvc
#ComponentScan("com.user.springsecuritydemo")
public class MainConfig implements WebMvcConfigurer {
#Bean
InternalResourceViewResolver configuInternalResourceViewResolver()
{
return new InternalResourceViewResolver("/WEB-INF/view/", ".jsp");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
registry
.addResourceHandler("/resources/**")
.addResourceLocations("/resources/");
}
}
public class SpringMVCDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses()
{
// TODO Auto-generated method stub
return null;
}
#Override
protected Class<?>[] getServletConfigClasses()
{
return new Class[] { MainConfig.class };
}
#Override
protected String[] getServletMappings()
{
return new String[] { "/" };
}
}
Okay, I ended up fixing it, so basically my jsp page is under my /WEBINF/ directory, which was not allowed for everyone to access therefore the browser couldn't get to the login page...
This is my SecurityFilterChain method:
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception
{
http
.authorizeHttpRequests()
.requestMatchers("/WEB-INF/**")
.permitAll();
http.authorizeHttpRequests()
.anyRequest()
.authenticated();
http
.formLogin()
.loginPage("/login")
.permitAll();
return http.build();
}

Spring Boot MVC with JWT Token

I am trying to build a web app with custom login page and access roles, but I wanna include JWT token also for the authentication and authorization.
Before including JWT, I configured security with access roles and ant matchers and custom login, I am able to access index view, when i wanna access "List of Students" i have to login with Admin credentials, when i wanna access "List of Subjects" i have to login with User credentials and it all works.
Now i wanna include JWT and i have all the JWT dependencies enabled, JwtRequest class, JwtResponse class, JwtUtil class and JwtRequestFilter. My security configuration is:
csrf().disable()
.authorizeRequests()
.antMatchers("/","/registration/**","/logout","/login").permitAll()
.antMatchers("/students/**").hasRole("Admin")
.antMatchers("/subjects/**").hasAnyRole("User","Admin")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.and()
.logout()
.logoutSuccessUrl("/login?logout");
and i also included:
// http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
//
// http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
I have my CustomUserDetailsClass:
public class CustomUserDetails implements UserDetails {
private User user;
public CustomUserDetails(User user) {
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities()
{
List<Role> roles = user.getRoles();
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for(Role role : roles)
{
authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName()));
}
return authorities;
}
#Override
public String getPassword() {
return user.getPassword();
}
#Override
public String getUsername() {
return user.getEmail();
}
I have my CustomUserDetailsService:
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User user = userRepository.findByEmail(email);
if(user == null)
{
throw new UsernameNotFoundException("User Not Found!");
}
return new CustomUserDetails(user);
}
I am not finding a solution to implement Token with my views. I can do it using postman with #RestController using a "/authenticate" method.
THanks in advance!
I wanna build an web app with user and admin. Users can access "list of subjects" and admin can access "list of students".
But im not being able to implement the JWT token with my WEb app
I had this kind of scenario in one of my projects, I uploaded it on GitHub so you can have a look, I fell it exactly what you want.
You just have to create a Authentication filter like in this project.
https://github.com/Parneet-Raghuvanshi/Spring-Boot-Basics
Do watch the security folder, it's all in there what you need.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()
.csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.GET, "/customer").hasRole("CUSTOMER")
.antMatchers(HttpMethod.GET, "/admin").hasRole("ADMIN")
.antMatchers(HttpMethod.GET, SecurityConstants.TEST_URL).permitAll()
.antMatchers(HttpMethod.GET, SecurityConstants.VERIFICATION_EMAIL_URL).permitAll()
.antMatchers(HttpMethod.POST, SecurityConstants.SIGNUP_URL).permitAll()
.antMatchers(HttpMethod.POST, SecurityConstants.PASSWORD_URL).permitAll()
.antMatchers(HttpMethod.POST, SecurityConstants.PASSWORD_RESET_URL).permitAll()
.anyRequest().authenticated()
.and().addFilter(getAuthenticationFilter())
.addFilter(new JwtFilter(authenticationManager()))
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
this is the configure() function of my web security from same project, so that you can have an idea about the filters used here.
And feel free to ask any doubt from repo too. Good Luck :)

Spring boot security login verify failed

I want to verify the user's identity when he or she send a localhost:8080/submit request, so I added the following to SecurityConfig class:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/submit").access("hasRole('WORKER')")
.antMatchers("/**").permitAll()
.and()
.formLogin()
.loginPage("/login")
.and()
.logout()
.logoutSuccessUrl("/")
.and()
.rememberMe()
.tokenValiditySeconds(4838400)
.key("workerKey");
}
I wish the page could redirect to localhost:8080/login when I input localhost:8080/submit in the address field. My Worker entity has the role "WORKER":
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority("WORKER"));
}
I can register an account and redirect to the login page when I input "localhost:8080/submit". But when I input the correct username and password, it responds to me an error page instead of submit page:
There was an unexpected error (type=Forbidden, status=403).
Forbidden
My submit page is simply a "welcome" word page. My mappings are
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String showLogin() {
return "login";
}
#RequestMapping(value = "/submit", method = RequestMethod.GET)
public String showSubmit() {
return "submit";
}
And when I input localhost:8080/submit again, it did not redirect to the login page this time. Instead, it redirects to the error page directly and show the same error. So what forbid me to redirect to the submit page?
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().fullyAuthenticated()
.antMatchers("/submit").hasRole("WORKER").and().formLogin().permitAll().and().logout().permitAll();
}
#Bean
public UserDetailsService userDetailsService() {
// ensure the passwords are encoded properly
#SuppressWarnings("deprecation")
UserBuilder users = User.withDefaultPasswordEncoder();
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(users.username("me").password("me").roles("WORKER").build());
return manager;
}
}
You can customize even more with your custom login page.
I have find the problem myself. I need to change the role "WORKER" in the Worker class to "ROLE_WORKER". Like this
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority("ROLE_WORKER"));
}
It seems I cannot simplify the role "ROLE_WORKER" into "WORKER" in the Worker class but can simplify it in the SecurityConfig class.

Auth websocket session after manual web auth

I am using Spring Security with STOMP WebSocket on SpringBoot. Auth on websocket worked fine with this config when I used simple login form:
#EnableWebSecurity
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/webjars/**", "/resources/**").permitAll()
.antMatchers("/register").anonymous()
.anyRequest()
.fullyAuthenticated()
.and()
.formLogin()
.loginPage("/login")
.successHandler(customLoginSuccessHandler)
.failureUrl("/login?error")
.permitAll()
.and()
.csrf().disable()
.logout().logoutSuccessHandler(customLogoutSuccessHandler);
}
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
#Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.nullDestMatcher().authenticated()
.simpTypeMatchers(CONNECT).authenticated()
.simpSubscribeDestMatchers(Channel.SYSTEM_ERROR.value()).permitAll()
.simpDestMatchers("/app/publish*").hasRole("USER")
.simpSubscribeDestMatchers("/user/**", "/topic/**", "/system/*").hasRole("USER")
.anyMessage().denyAll();
}
But when I wanted to manually auth client after register new user in RegisterController:
#RequestMapping(value = "/register", method = RequestMethod.POST)
public String signup(#Valid #ModelAttribute SignupForm signupForm, Errors errors) {
if (errors.hasErrors()) {
return SIGNUP_VIEW_NAME;
}
User user = signupForm.createAccount();
try {
userService.persist(user);
} catch (EntityExistsException ex) {
errors.rejectValue("login", "user.exists");
return SIGNUP_VIEW_NAME;
}
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(user, null, Collections.singletonList(new SimpleGrantedAuthority("USER"))));
return "redirect:/";
}
I've got problem with auth websocket. When I get redirected to page where websocket connects I am getting org.springframework.security.access.AccessDeniedException: Access is denied
So. Problem was in define Role. In controller when I defined new SimpleGrantedAuthority("USER") it should be "ROLE_USER" because Spring adds refix ROLLE_ by default. Sure we can change default behaviour of this by add next in WebSecurity configuration
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**", "/favicon.ico");
web.expressionHandler(new DefaultWebSecurityExpressionHandler() {
#Override
protected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, FilterInvocation fi) {
WebSecurityExpressionRoot root = (WebSecurityExpressionRoot) super.createSecurityExpressionRoot(authentication, fi);
root.setDefaultRolePrefix(""); //remove the prefix ROLE_
return root;
}
});
}
. Yes, dummy mistake but so common. So I will leave it here

Not able to recognize user ROLE when redirecting page using Spring Security

I am working on my project with Spring security and Thymeleaf. I have basic Spring Security integration.
SecurityConfig.java
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
private DataSource dataSource;
#Autowired
public void configureGlobal (AuthenticationManagerBuilder auth) throws Exception
{
auth
.jdbcAuthentication()
.dataSource(dataSource);
}
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/success", true)
.and()
.httpBasic();
}
}
SecurityWebApplicationInitializer.java
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer
{
public SecurityWebApplicationInitializer(){
super(SecurityConfig.class);
}
}
Controller.java
#Controller
public class HomeController {
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String loginPage(Model model) {
return "login";
}
#RequestMapping("/success")
public String loginPageRedirect(HttpServletRequest httpServletRequest){
if(httpServletRequest.isUserInRole("ROLE_ADMIN")) {
return "index1";
} else if(httpServletRequest.isUserInRole("ROLE_USER")) {
return "index2";
} else {
return "index3";
}
}
}
When I have successful login my user is redirected, but to wrong page. My user has role ROLE_USER but method loginPageRedirect is redirecting him to page index3 when it should be index2. I guess my user role is not recognize. How can I do that? Should I add something as parameter to loginPageRedirect so it recognizes role?
I found solution that works for me.
I edited my loginPageRedirect method like this:
#RequestMapping("/success")
public void loginPageRedirect(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException, ServletException {
String role = authResult.getAuthorities().toString();
if(role.contains("ROLE_ADMIN")){
response.sendRedirect(response.encodeRedirectURL(request.getContextPath() + "/index1"));
}
else if(role.contains("ROLE_USER")) {
response.sendRedirect(response.encodeRedirectURL(request.getContextPath() + "/index2"));
}
}
Hope it helps someone with same issue :)

Resources