Skip to content

Instantly share code, notes, and snippets.

@bmchild
Created January 19, 2012 21:15
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save bmchild/1642655 to your computer and use it in GitHub Desktop.
Save bmchild/1642655 to your computer and use it in GitHub Desktop.
Custom regex lookup access Expression with Spring Security 3.1. The lookup, hasRegexRole, is available on the JSP or on the method.
<!--
Enable class- and method-level security via annotations
-->
<sec:global-method-security pre-post-annotations="enabled" secured-annotations="enabled">
<sec:expression-handler ref="expressionHandler"/>
</sec:global-method-security>
<!--
WebSecurityExpressHandler for Spring Security JSP tags
-->
<bean id="webSecurityExpressionHandler" class="com.bmchild.security.access.expression.CustomWebSecurityExpressionHandler"/>
<!-- Custom Expression Handler -->
<bean id="expressionHandler" class="com.bmchild.security.access.expression.CustomMethodSecurityExpressionHandler" />
package com.bmchild.security.access.expression;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.log4j.Logger;
import org.springframework.security.access.expression.SecurityExpressionRoot;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.core.Authentication;
/**
* @author bchild
*
*/
public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
Logger LOGGER = Logger.getLogger(CustomMethodSecurityExpressionHandler.class);
public CustomMethodSecurityExpressionHandler() {
super();
}
/* (non-Javadoc)
* @see org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler#createSecurityExpressionRoot(org.springframework.security.core.Authentication, org.aopalliance.intercept.MethodInvocation)
*/
@Override
protected SecurityExpressionRoot createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) {
CustomSecurityExpressionRoot root = new CustomSecurityExpressionRoot(authentication);
root.setPermissionEvaluator(getPermissionEvaluator());
return root;
}
}
package com.bmchild.security.access.expression;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import org.springframework.security.access.expression.SecurityExpressionRoot;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.util.IpAddressMatcher;
/**
*
* Security Expression evaluator for wildcard use
*
* @author bchild
*
*/
public class CustomSecurityExpressionRoot extends SecurityExpressionRoot {
Logger LOGGER = Logger.getLogger(CustomSecurityExpressionRoot.class);
private Set<String> userRoles;
/*
* This is for emulating WebSecurityExpressionRoot
*/
public HttpServletRequest request;
/**
* @param a
*/
public CustomSecurityExpressionRoot(Authentication a) {
super(a);
}
public CustomSecurityExpressionRoot(Authentication a, FilterInvocation fi) {
super(a);
this.request = fi.getRequest();
}
/**
* Checks given roles against the given regex expression
*
* @param regex
* to match agains roles
* @return true if at least 1 authority matches the regex, otherwise false
*/
public boolean hasRegexRole(String regex) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("hasRegexRole: " + regex);
}
boolean found = false;
Set<String> authorities = getCustomAuthoritySet();
for (String authority : authorities) {
if (authority.matches(regex)) {
found = true;
break;
}
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("hasRegexRole returns " + found);
}
return found;
}
/**
*
* @see org.springframework.security.web.access.expression.
* WebSecurityExpressionRoot.hasIpAddress(String) Takes a specific IP
* address or a range using the IP/Netmask (e.g. 192.168.1.0/24 or
* 202.24.0.0/14).
*
* @param ipAddress
* the address or range of addresses from which the request must
* come.
* @return true if the IP address of the current request is in the required
* range.
*/
public boolean hasIpAddress(String ipAddress) {
return (new IpAddressMatcher(ipAddress).matches(request));
}
/**
* Note: this does not return hierchacal roles like
* org.springframework.security
* .access.expression.SecurityExpressionRoot.getAuthoritySet()
*
* @return set of authorities
*/
private Set<String> getCustomAuthoritySet() {
if (userRoles == null) {
userRoles = new HashSet<String>();
Collection<? extends GrantedAuthority> userAuthorities = authentication.getAuthorities();
userRoles = AuthorityUtils.authorityListToSet(userAuthorities);
}
return userRoles;
}
}
package com.bmchild.security.access.expression;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Collection;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
/**
* @author bchild
*
*/
@RunWith(MockitoJUnitRunner.class)
public class CustomSecurityExpressionRootTest {
private CustomSecurityExpressionRoot expression;
private Authentication auth;
@Before
public void before() {
auth = mock(Authentication.class);
expression = new CustomSecurityExpressionRoot(auth);
}
/**
* Test method for {@link com.crengland.security.access.expression.CustomSecurityExpressionRoot#hasRegexRole(java.lang.String)}.
*/
@Test
public void testHasRegexRole() {
// Mock
final Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add( new SimpleGrantedAuthority("ROLE_1_ADMIN") );
authorities.add( new SimpleGrantedAuthority("ROLE_2_ADMIN") );
authorities.add( new SimpleGrantedAuthority("ROLE_3_ADMIN") );
authorities.add( new SimpleGrantedAuthority("ROLE_1_NONADMIN") );
when(auth.getAuthorities()).thenAnswer( new Answer<Collection<GrantedAuthority>>() {
@Override
public Collection<GrantedAuthority> answer(InvocationOnMock invocation) throws Throwable {
return authorities;
}
});
// Test
assertTrue( expression.hasRegexRole("ROLE_1_ADMIN") );
assertTrue( expression.hasRegexRole("ROLE_.+_ADMIN") );
assertFalse( expression.hasRegexRole("ROLE__ADMIN") );
assertTrue( expression.hasRegexRole("ROLE_[\\d]+_NONADMIN") );
// Verify
verify(auth).getAuthorities();
}
}
package com.bmchild.security.access.expression;
import org.springframework.security.access.expression.SecurityExpressionRoot;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
/**
* @author bchild
*
*/
public class CustomWebSecurityExpressionHandler extends DefaultWebSecurityExpressionHandler {
@Override
protected SecurityExpressionRoot createSecurityExpressionRoot(Authentication authentication, FilterInvocation fi) {
CustomSecurityExpressionRoot root = new CustomSecurityExpressionRoot(authentication, fi);
root.setPermissionEvaluator(getPermissionEvaluator());
return root;
}
}
@ctech-michaelkhazanov
Copy link

@configuration
@EnableWebSecurity
@order(2)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
@Qualifier("WebUserDetailsService")
private UserDetailsService userDetailsService;

@Autowired
private BasePermissionEvaluator basePermissionEvaluator;

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .csrf().disable()
        .authorizeRequests()
            .antMatchers("/css/**").permitAll()
            .antMatchers("/js/**").permitAll()
            .antMatchers("/images/**").permitAll()
            .anyRequest().authenticated()
            .and()
        .formLogin()
            .loginPage("/login").permitAll()
            .and()
            .logout().permitAll();
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService).passwordEncoder(new ShaPasswordEncoder(256));
}

/**
 * override web security expression handler to use our custom permission evaluator.
 * It is going to be used in a jsp by this tag:
 * <sec:authorize access="hasPermission('#user','VIEW_LINK_FORMS')">
 */
@Override
public void configure(WebSecurity web) throws Exception {
    ((DefaultWebSecurityExpressionHandler) web.getExpressionHandler()).setPermissionEvaluator(basePermissionEvaluator);
}

}

@component
public class BasePermissionEvaluator implements PermissionEvaluator {

@Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
    boolean hasPermission = false;
    if (authentication != null && permission instanceof String) {

        String strPermission = (String) permission;

        UserVO user = (UserVO) authentication.getPrincipal();
        Map<String, PermissionVO> userPermissions = user.getPermissions();

        if (userPermissions !=null){
            PermissionVO objpermision = userPermissions.get(strPermission.trim());

            if (objpermision != null) {
                if (objpermision.getPermissionName().toUpperCase().equals(permission)) {
                    hasPermission = true;
                }
            }
        }

    } else {
        hasPermission = false;
    }
    return hasPermission;
}

@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
    // TODO Auto-generated method stub
    return true;
}

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment