Rechte und Rollen
Mit dem CoreMedia JavaEditor können Berechtigungsgruppen oder Rollen erstellt und Nutzern zugeordnet werden. Über die Gruppen können Berechtigungen sehr feingranular auf Dokumentebene eingestellt werden.
Bei der Authentifizierung wird das Nutzerobjekt mit seinen Berechtigungen (GrantedAuthority) geladen und im SecurityContext abgelegt. Für die Autorisierung kann in allen Schichten der Architektur darauf zugegriffen werden.
Autorisierung im Template
Wenn es sich um einfache rollenbasierte Autorisierung handelt, kann die authorize-Taglib aus dem Framework benutzt werden.
<authz:authorize ifAllGranted="ROLE_SUPERVISOR"> <td> <A HREF="del.htm?id=<c:out value="${contact.id}"/>">Del</A> </td> </authz:authorize>
Die Autorisierung mit Berechtigungsgruppen (Bean-basierte Autorisierung) ist komplexer. Je nach Berechtigung des Nutzers sollen unterschiedliche Inhalte aus dem CMS dargestellt werden. Es empfiehlt sich, die Funktionalität für die Prüfung der Berechtigung in eine eigene Komponente (AuthorizationUtils
) auszulagern, da diese auch z.B. bei den Controllern verwendet werden kann.
AuthorizationUtils
public class AuthorizationUtils{ ... public boolean isAccessAllowed(Content content){ GrantedAuthority[] grants = null; if (SecurityContextHolder.getContext().getAuthentication() != null) { grants = SecurityContextHolder .getContext().getAuthentication().getAuthorities(); } int length = (grants != null) ? grants.length : 0; String[] groupnames = new String[length]; for (int i = 0; i < length; i++) { groupnames[i] = grants[i].getAuthority(); } LinkedList groups = getGroups(groupnames); if (groups != null && getAccessControl().mayRead(content, groups)) { isAccessAllowed = true; } else { isAccessAllowed = false; } return isAccessAllowed; } private LinkedList getGroups(String[] groupNames) { LinkedList groups = new LinkedList(); for (int i = 0; groupNames != null && i < groupNames.length; i++) { Group group = userRepository.getGroupByName(groupName, null); if (group != null) { groups.add(group); } } return groups; } private AccessControl getAccessControl() throws MipException { AccessControl accessControl = getRepository().getAccessControl(); return accessControl ; } ...
}
Authorize-Tag Library
public class Authorize extends TagSupport { private ContentBean contentBean; public int doStartTag() { if (null == bean) { return SKIP_BODY; } ApplicationContext context = getContext(pageContext); AuthorizationUtils authorizationUtils = (AuthorizationUtils) context.getBean("authorizationUtils"); try { if (authorizationUtils.isAccessAllowed(bean.getContent(), null, (HttpServletRequest) pageContext .getRequest())) { return EVAL_BODY_INCLUDE; } } catch (Exception e) { e.printStackTrace(); } return SKIP_BODY; } protected ApplicationContext getContext(PageContext pageContext) { ServletContext servletContext = pageContext.getServletContext(); return WebApplicationContextUtils .getRequiredWebApplicationContext(servletContext); } public ContentBean getContentBean() { return contentBean; } public void setContentBean(ContentBean contentBean) { contentBean = contentBean; } }
Einbindung im Template
<mse:authorize contentBean="${self}"> <c:out value="${self.name}" /> </mse:authorize>
Autorisierung im Controller
Auch im Controller kann man den bereits vorgestellten Bean-basierten Autorisierungsmechanismus verwenden, um z.B. komplette Seitenaufrufe zu verhindern.
SecureContentViewController
public class SecureContentViewController extends ContentViewController { private AuthorizationUtils authorizationUtils; protected Object resolveBean(String controllerPathInfo, Map parameters, HttpServletRequest request) { ... } protected String resolveView(String controllerPathInfo, Map parameters, HttpServletRequest request) { ... } protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { String pathInfo = controllerPathInfo(HttpServletRequest request); Object bean = resolveBean(pathInfo, request.getParameterMap(), request); ContentBean contentBean = (ContentBean) bean; if (!authorizationUtils.isAccessAllowed(contentBean.getContent())) { // Fehlerbehandlung return new ModelAndView(new RedirectView(LOGIN_URL)); } return super.handleRequestInternal(request, response); } ... }
Autorisierung auf Business-Methoden
Das Thema methodenbasierte Security kann man elegant mit den Mitteln der aspektorientierten Programmierung (AOP) lösen. Neben Frameworks wie aspectJ bietet auch Spring mit Dynamic Proxys eine AOP-Implementierung.
CheckPermissionInterceptor
public class CheckPermissionInterceptor implements MethodInterceptor { public Object invoke(MethodInvocation aMethodInvocation) throws Throwable { initSecureMethodsMap(); String currentMethode = aMethodInvocation.getMethod().getName(); // ist SecureMethode if (secureMethodsMap.containsKey(currentMethode)) { Authentication authentication = SecurityContextHolder.getContext() .getAuthentication(); GrantedAuthority[] auth = authentication.getAuthorities(); int length = (auth != null) ? auth.length : 0; String[] roleNames = new String[length]; for (int i = 0; auth != null && i < auth.length; i++) { roleNames[i] = auth[i].getAuthority(); } String roles = (String) secureMethodsMap.get(currentMethode); if(!checkPermission(roles, roleNames){ // Keine Rechte throw new AccessDeniedException("User hat nicht genügend Rechte"); } } return aMethodInvocation.proceed(); } private boolean checkPermission(String allowedRoles, String[] availableRoles){ for (int i = 0; i < availableRoles.length; i++) { if(allowedRoles.indexOf(availableRoles[i])!=-1){ return true; } } return false; } }
In der Spring-Konfiguration wird ein Dynamic Proxy mit dem Namen des Services konfiguriert. Der Proxy kennt den Service unter der Property target. Die Service-Aufrufe werden so durch den Proxy abgefangen. Nun kommt der Interceptor ins Spiel. Er prüft für konfigurierte Methoden die Berechtigung. Ist alles o.k., wird die Anfrage an den eigentlichen Service delegiert.
Spring-Konfiguration
<beans> <bean id="aServiceTarget" class="de.mse.business.ServiceImpl"> ... </bean> <bean id="aService" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>de.mse.business.IService</value> </property> <property name="target"> <ref bean="aServiceTarget"/> </property> <property name="interceptorNames"> <list> <value>checkPermissionInterceptor</value> </list> </property> </bean> <bean id="checkPermissionInterceptor" class="de.mse.security.CheckPermissionInterceptor"> <property name="secureMethods"> <value> methode1=ADMIN methode2=ADMIN, REDAKTEUR ... </value> </property> </bean> </beans>
Links
- ACEGI Security
- Spring Security Framework
- Tag Libraries Tutorial
- Aspektorientierten Programmierung (AOP)
- aspectJ
- Spring AOP-Implementierung
- Teil1: Authentifizierung
The post CoreMedia und Spring Security verheiratet – Teil 2: Autorisierung appeared first on Communardo Techblog.