5

I am developing a application for test the resources of the Spring Security, and in the current state of it, I need one solution to implement Roles AND Permissions for my methods. I already using this annotation:

@Secured("ROLE_ADMIN")

to define which role should be able to access each method. In my database, I have this structure:

which means for each Role I can have a list of permissions. Anyone can point me a solution for define both restrictions for each one of my methods? I am configuring the Spring Security via Java code, this way:

https://github.com/klebermo/webapp2/tree/master/src/com/spring/webapp/lojavirtual/config/security

UPDATE

Following the presented sugestions, I change my AuthenticationService (which extends UserDetailsService) to this:

@Service
public class AuthenticationService implements UserDetailsService {

    @Autowired
    private UsuarioHome accountDao;

    @Override
    @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Usuario account = accountDao.findByField("login", username);

        if(account==null) {
            System.out.println("No such user: " + username);
            throw new UsernameNotFoundException("No such user: " + username);
        } else if (account.getAutorizacao().isEmpty()) {
            System.out.println("User " + username + " has no authorities");
            throw new UsernameNotFoundException("User " + username + " has no authorities");
        }

        List<Permission> lista = new ArrayList<Permission>();
        for(int i=0; i<account.getAutorizacao().size(); i++) {
            for(int j=0; j<account.getAutorizacao().get(i).getPermissao().size(); j++) {
                lista.add(account.getAutorizacao().get(i).getPermissao().get(j));
            }
        }

        boolean accountIsEnabled = true;
        boolean accountNonExpired = true;
        boolean credentialsNonExpired = true;
        boolean accountNonLocked = true;

        return new User(account.getLogin(), account.getSenha(), accountIsEnabled, accountNonExpired, credentialsNonExpired, accountNonLocked, getAuthorities(lista));
    }

    public List<String> getRolesAsList(List<Permission> list) {
        List <String> rolesAsList = new ArrayList<String>();
        for(Permission role : list){
            rolesAsList.add(role.getNome());
        }
        return rolesAsList;
    }

    public static List<GrantedAuthority> getGrantedAuthorities(List<String> roles) {
        List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
        for (String role : roles) {
            authorities.add(new SimpleGrantedAuthority(role));
        }
        return authorities;
    }

    public Collection<? extends GrantedAuthority> getAuthorities(List<Permission> list) {
        List<GrantedAuthority> authList = getGrantedAuthorities(getRolesAsList(list));
        return authList;
    }

and in my methods, I change to that:

@Controller
@RequestMapping(value="privado")
public class PrivadoController {

    @RequestMapping(value="admin")
    @Secured("admin_main")
    public ModelAndView admin() {
        ModelAndView mav = new ModelAndView();
        mav.setViewName("privado/admin");
        return mav;
    }

    @RequestMapping(value="customer")
    @Secured("customer_main")
    public ModelAndView customer() {
        ModelAndView mav = new ModelAndView();
        mav.setViewName("privado/customer");
        return mav;
    }

}

But now when I try acess the page, I get my no_permit page. what's wrong now?

UPDATE

after some changes, I finally get to this code:

AuthenticationService.java

@Service
public class AuthenticationService implements UserDetailsService {

    @Autowired
    private UsuarioHome accountDao;

    @Override
    @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Usuario account = accountDao.findByField("login", username);

        if(account==null) {
            System.out.println("No such user: " + username);
            throw new UsernameNotFoundException("No such user: " + username);
        } else if (account.getAutorizacao().isEmpty()) {
            System.out.println("User " + username + " has no authorities");
            throw new UsernameNotFoundException("User " + username + " has no authorities");
        }

        List<Permission> lista = new ArrayList<Permission>();
        int max = account.getAutorizacao().size();
        for(int i=0; i<max; i++) {
            for(int j=0; j<max; j++) {
                lista.add(account.getAutorizacao().get(i).getPermissao().get(j));
            }
        }

        boolean accountIsEnabled = true;
        boolean accountNonExpired = true;
        boolean credentialsNonExpired = true;
        boolean accountNonLocked = true;

        return new User(account.getLogin(), account.getSenha(), accountIsEnabled, accountNonExpired, credentialsNonExpired, accountNonLocked, getAuthorities(lista));
    }

    public List<String> getRolesAsList(List<Permission> list) {
        List <String> rolesAsList = new ArrayList<String>();
        for(Permission role : list){
            rolesAsList.add(role.getNome());
        }
        return rolesAsList;
    }

    public static List<GrantedAuthority> getGrantedAuthorities(List<String> roles) {
        List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
        for (String role : roles) {
            authorities.add(new SimpleGrantedAuthority(role));
        }
        return authorities;
    }

    public Collection<? extends GrantedAuthority> getAuthorities(List<Permission> list) {
        List<GrantedAuthority> authList = getGrantedAuthorities(getRolesAsList(list));
        return authList;
    }

}

In my controller, I try use either the annotation @Secured or @PreAuthorize, in this way:

@Controller
@RequestMapping(value="privado")
public class PrivadoController {

    @RequestMapping(value="admin")
    //@Secured("admin_main")
    @PreAuthorize("hasRole('admin_main')")
    public ModelAndView admin() {
        ModelAndView mav = new ModelAndView();
        mav.setViewName("privado/admin");
        return mav;
    }

    @RequestMapping(value="customer")
    //@Secured("customer_main")
    @PreAuthorize("hasRole('customer_main')")
    public ModelAndView customer() {
        ModelAndView mav = new ModelAndView();
        mav.setViewName("privado/customer");
        return mav;
    }

}

But I am not getting access to any user, despite informing the right credencials. What's wrong in this current scenario?

UPDATE 2

Ok, it happens the problem with the access denied was becasue an error in the code in my CustomAuthenticationSuccessHandler, which now it is like that:

public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication auth) throws IOException, ServletException {
        HttpSession session = request.getSession();
        SavedRequest savedReq = (SavedRequest) session.getAttribute(WebAttributes.ACCESS_DENIED_403);

        boolean isAdmin = false;
        boolean isUser = false;

        if (savedReq == null) {
            Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
            for (GrantedAuthority grantedAuthority : authorities) {
                if(grantedAuthority.getAuthority().equals("admin_main"))
                    isAdmin = true;
                else if(grantedAuthority.getAuthority().equals("customer_main"))
                    isUser = true;
            }

            if(isAdmin)
                response.sendRedirect(request.getContextPath() + "/privado/admin");
            else if(isUser)
                response.sendRedirect(request.getContextPath() + "/privado/customer");
            else
                response.sendRedirect(request.getContextPath() + "/erro/no_permit");
        }
        else {
            response.sendRedirect(savedReq.getRedirectUrl());
        }
    }

}

But now when I try login in my appliation, I get the error described in this topic:

SpelEvaluationException: EL1004E:(pos 0): Method call: Method hasPermission(java.lang.String) cannot be found on MethodSecurityExpressionRoot type

Any ideas in how to solve this one?

Community
  • 1
  • 1
Kleber Mota
  • 8,521
  • 31
  • 94
  • 188
  • this might help http://stackoverflow.com/questions/6357579/spring-security-with-roles-and-permissions – gouki Apr 15 '14 at 00:29
  • What the link describes is basicly what I have implemented now, but how I duse it? Meaning: for each methods, how I define which role and permission the user should have to access them? that's my main question now. – Kleber Mota Apr 15 '14 at 00:36
  • Like I said, I use now **@Secured("ROLE_ADMIN")**. How I modify my code to include the permissions? – Kleber Mota Apr 15 '14 at 00:37
  • 3
    The fact that something starts with `ROLE_` doesn't make it a role, it actually is a permission. Your `role` is nothing more then a container of permissions, so instead of specifying `@Secured("ROLE_ADMIN")` do something like `@Secured("YOUR_PERMISSION")`, then configure Spring Security in such a way that your roles are ignored and only your permissions are taken into account. (Or maybe easier, fix that in your `getAuthorities` method on your `User` (assuming that it implements `UserDetails`)). – M. Deinum Apr 15 '14 at 05:18
  • Ok, I just made some changes in my code (see the update). But now I am being denied to access the page. what I made wrong now, can you tell me? – Kleber Mota Apr 15 '14 at 10:23

0 Answers0