简体   繁体   中英

Spring Security Java configuration for authenticated users with a role

I am trying to configure Spring Security using Java configuration with Spring Boot.

My WebSecurityConfig class has a configure method like this

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .antMatchers("/", "/login", "/css/**", "/js/**", "/img/**", "/bootstrap/**" ).permitAll()
            .antMatchers("/individual", "/application", "/upload").hasRole("USER")
        .and()
            .formLogin()
                .loginPage("/login")
                .successHandler(successHandler);

}

I get the login page fine for unauthenticated users, and the user authenticates. However, when trying to access the url /individual I get a 403 error and this in the log

2015-11-12 13:59:46.373 DEBUG 34468 --- [nio-8090-exec-6] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/individual'; against '/'
2015-11-12 13:59:46.373 DEBUG 34468 --- [nio-8090-exec-6] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/individual'; against '/login'
2015-11-12 13:59:46.373 DEBUG 34468 --- [nio-8090-exec-6] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/individual'; against '/css/**'
2015-11-12 13:59:46.373 DEBUG 34468 --- [nio-8090-exec-6] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/individual'; against '/js/**'
2015-11-12 13:59:46.374 DEBUG 34468 --- [nio-8090-exec-6] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/individual'; against '/img/**'
2015-11-12 13:59:46.374 DEBUG 34468 --- [nio-8090-exec-6] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/individual'; against '/bootstrap/**'
2015-11-12 13:59:46.374 DEBUG 34468 --- [nio-8090-exec-6] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/individual'; against '/individual'
2015-11-12 13:59:46.374 DEBUG 34468 --- [nio-8090-exec-6] o.s.s.w.a.i.FilterSecurityInterceptor    : Secure object: FilterInvocation: URL: /individual; Attributes: [hasRole('ROLE_USER')]
2015-11-12 13:59:46.374 DEBUG 34468 --- [nio-8090-exec-6] o.s.s.w.a.i.FilterSecurityInterceptor    : Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@4c2eda81: Principal: org.springframework.security.core.userdetails.User@4c0ab982: Username: foo@bar.com; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@fffc7f0c: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 6E0BF830C070912EAC0050D1285D5CF9; Granted Authorities: USER
2015-11-12 13:59:46.374 DEBUG 34468 --- [nio-8090-exec-6] o.s.s.access.vote.AffirmativeBased       : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@6f192fd7, returned: -1
2015-11-12 13:59:46.386 DEBUG 34468 --- [nio-8090-exec-6] o.s.s.w.a.ExceptionTranslationFilter     : Access is denied (user is not anonymous); delegating to AccessDeniedHandler

if I replace .hasRole("USER") with .authenticated() it works and the user gets the page desired, but no role is being checked.

I cannot figure out how to specify that the user must be authenticated and have the role USER. I have looked at many examples and tried many combinations but I cannot get it to work as desired. Most of the examples refer to older XML based configuration as well, which I want to avoid.

What do I need to do to specify that certain URL patterns can be accessed with authenticated users with certain roles?

As requested, the success handler method looks like this

@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication auth)
        throws IOException, ServletException {

    // Here we want to check whether the user has already started the process and redirect to the appropriate stage

    log.info("User {} has been authenticated", auth.getName());

    if(auth.getName().equals("foo@bar.com"))
    {
        redirectStrategy.sendRedirect(request, response, "/individual");
        return;
    }


    redirectStrategy.sendRedirect(request, response, "/application");

}

Note that this is early stage WIP, per the comments in the code. I know this works as the log message appears and without the role check in the WebSecurity class the desired page is served.

EDIT : noticed a typo in the question referring to roleService.getDefaultRole(). This was a mistake and has been corrected.

I think your logged in user foo@bar.com has the wrong authorities set. In the log you can see the authorities are ['USER'], but since you are working with roles it should be ['ROLE_USER']. Where do you define the authorities for foo@bar.com?

Otherwise try switching

.antMatchers("/individual", "/application", "/upload").hasRole("USER")

to

.antMatchers("/individual", "/application", "/upload").hasAuthority("USER")

I have solved my own question, thanks in part to the various suggestions here making me research it differently. Also noted that FrontierPsychiatrist provided the answer as well, but I did not spot the response while I was working on it.

The problem is that Spring interchangably refers to ROLES and AUTHORITIES.

Having implemented the UserDetailsService interface to allow Spring to load user records from the application database, I was creating an instance of org.springframework.security.core.userdetails.User and setting the granted authorities . It can be seen in the log statements that the User object had that - Granted Authorities: USER , but then my security config was checking for a role, and failing. The solution, simply, was to check for an authority instead. I have also cleaned up the config to separate the URL patterns to be ignored from security from the authentication & authorisation. Complete code snippet below

@Override
public void configure(WebSecurity web) throws Exception {
    web
        .ignoring()
            .antMatchers( "/css/**", "/js/**", "/img/**", "/bootstrap/**");
}

@Override
protected void configure(HttpSecurity http) throws Exception {

    http
        .authorizeRequests()
            .antMatchers("/individual","/application", "/upload").hasAuthority("USER")
            .and()
        .formLogin()
            .loginPage("/login")
            .successHandler(successHandler)
            .permitAll()
            .and()
        .logout()
            .deleteCookies("remove")
            .logoutSuccessUrl("/login?logout")
            .permitAll();   
}

I really hope this helps others when using Java based configuration with Spring 4 MVC.

Modify your configuration something like this.

http.authorizeRequests().antMatchers("/individual","/application", "/upload")
                .hasRole("USER").and().authorizeRequests().anyRequest()
                .authenticated().and()
        .formLogin()
            .loginPage("/login")
            .successHandler(successHandler);

and if you don't want all the request to be authenticated you can replace anyRequest() method with antMatchers("/urls-goes-here")

Here's a tutorial that does what you're trying to implement:

http://www.jcombat.com/spring/custom-authentication-success-handler-with-spring-security

I'm guessing you forgot to set the session attributes in your handler:

    HttpSession session = request.getSession();
    session.setAttribute("uname", authUser.getUsername());  
    session.setAttribute("authorities", authentication.getAuthorities()); 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM