Posts mit dem Label Spring werden angezeigt. Alle Posts anzeigen
Posts mit dem Label Spring werden angezeigt. Alle Posts anzeigen

Freitag, 1. November 2013

CAS (Central Authentication Service) mit Spring Security - Teil 3

Beispiel Klasse: /src/java/cas/MyUserDetails.java


 package de.test.cas;  
 import java.io.Serializable;  
 import java.util.Collection;  
 import java.util.HashSet;  
 import java.util.Set;  
 import javax.persistence.EntityManager;  
 import org.apache.log4j.Logger;  
 import org.springframework.security.core.GrantedAuthority;  
 import org.springframework.security.core.authority.SimpleGrantedAuthority;  
 import org.springframework.security.core.userdetails.UserDetails;  
 /*  
  *   
  * Assign all authenticated users ROLE_AUTHENTICATED  
  *   
  */  
 public class MyUserDetails implements Serializable, UserDetails {  
      private static final long serialVersionUID = 1L;  
      private final String username;  
      private final Set<Long> permissionsForUsers;  
      static final Logger logger = Logger.getLogger(MyUserDetails.class);  
      private final Collection<GrantedAuthority> authorities;  
      public MyUserDetails(Collection<GrantedAuthority> authorities, String username, Set<Long> permissionsForUsers) {  
           this.username=username;  
           this.authorities = authorities;  
           this.permissionsForUsers = permissionsForUsers;  
      }  
      @Override  
      public String toString() {  
           return this.username;  
      }  
      public Set<Long> getPermissionsForUsers() {  
           return permissionsForUsers;  
      }  
      @Override  
      public Collection<GrantedAuthority> getAuthorities() {  
           return authorities;  
      }  
      @Override  
      public String getPassword() {  
           return null;  
      }  
      @Override  
      public String getUsername() {  
           return username;  
      }  
      @Override  
      public boolean isAccountNonExpired() {  
           return true;  
      }  
      @Override  
      public boolean isAccountNonLocked() {  
           return true;  
      }  
      @Override  
      public boolean isCredentialsNonExpired() {  
           return true;  
      }  
      @Override  
      public boolean isEnabled() {  
           return true;  
      }  
 }  


Beispiel Klasse: /src/java/cas/MyUserService.java


 package de.test.cas;  
 import java.math.BigDecimal;  
 import java.util.HashSet;  
 import java.util.List;  
 import java.util.Set;  
 import javax.persistence.EntityManager;  
 import javax.persistence.NoResultException;  
 import javax.persistence.PersistenceContext;  
 import javax.persistence.Query;  
 import javax.persistence.criteria.CriteriaBuilder;  
 import javax.persistence.criteria.CriteriaQuery;  
 import javax.persistence.criteria.Root;  
 import org.apache.log4j.Logger;  
 import org.springframework.dao.DataAccessException;  
 import org.springframework.security.core.GrantedAuthority;  
 import org.springframework.security.core.authority.SimpleGrantedAuthority;  
 import org.springframework.security.core.userdetails.UserDetails;  
 import org.springframework.security.core.userdetails.UserDetailsService;  
 import org.springframework.security.core.userdetails.UsernameNotFoundException;  
 import de.test.db.domain.Assistant;  
 import de.test.db.domain.Transformable;  
 import de.test.db.domain.User;  
 public class MyUserService implements UserDetailsService {  
      private List<String> predefinedAdminAccounts = null;  
      static final Logger logger = Logger.getLogger(UserDetailsService.class);  
      private EntityManager entityManager = null;  
      public EntityManager getEntityManager() {  
           return entityManager;  
      }  
      @PersistenceContext  
      public void setEntityManager(EntityManager entityManager) {  
           this.entityManager = entityManager;  
      }  
      public List<String> getPredefinedAdminAccounts() {  
           return predefinedAdminAccounts;  
      }  
      public void setPredefinedAdminAccounts(List<String> predefinedAdminAccounts) {  
           this.predefinedAdminAccounts = predefinedAdminAccounts;  
      }  
      @Override  
      public UserDetails loadUserByUsername(String username)  
                throws UsernameNotFoundException, DataAccessException, NoResultException {  
           logger.info("!!! loadUserByUsername !!!");  
           Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();   
           Set<Long> permissionsForUsers = new HashSet<Long>();  
           if(predefinedAdminAccounts.contains(username)) {  
                authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));  
           } else {  
                CriteriaBuilder cb = entityManager.getCriteriaBuilder();  
                CriteriaQuery<User> uq = cb.createQuery(User.class);  
                Root<User> ur = uq.from(User.class);  
                uq.select(ur).where(cb.equal(ur.get("accountName"), username));  
                List<User> uresult = entityManager.createQuery(uq).getResultList();  
                User user = null;  
                if(!uresult.isEmpty()) {  
                     user = uresult.get(0);  
                }  
                if(user != null) {  
                     permissionsForUsers.add(user.getId());  
                     logger.info(user.getAccountName() + " " + user.getId());  
                     authorities.add(new SimpleGrantedAuthority("ROLE_USER"));  
                }  
                CriteriaQuery<Assistant> aq = cb.createQuery(Assistant.class);  
                Root<Assistant> ar = aq.from(Assistant.class);  
                aq.select(ar).where(cb.equal(ar.get("accountName"), username));  
                List<Assistant> aresult = entityManager.createQuery(aq).getResultList();  
                Assistant assistant = null;  
                if(!aresult.isEmpty()) {  
                     assistant = aresult.get(0);  
                }  
                if(assistant != null) {  
                     authorities.add(new SimpleGrantedAuthority("ROLE_ASSISTANT"));  
                     Query queryForPrincipals = getEntityManager().createNativeQuery("select userId from USERS_ASSISTANTS where assistantId=:assistantId");  
                     queryForPrincipals.setParameter("assistantId", assistant.getId());  
                     List<BigDecimal> ids = queryForPrincipals.getResultList();  
                     for(BigDecimal b : ids)  
                          permissionsForUsers.add(b.longValue());  
                }  
           }  
           return      new MyUserDetails(authorities, username, permissionsForUsers);  
      }  
      private <T extends Transformable<Long>> T getPrincipal(Class<T> c, String username) {  
           CriteriaBuilder cb = entityManager.getCriteriaBuilder();  
           CriteriaQuery<T> cq = cb.createQuery(c);  
           Root<T> r = cq.from(c);  
           cq.select(r).where(cb.equal(r.get("accountName"), username));  
           List<T> result = entityManager.createQuery(cq).getResultList();  
           if(!result.isEmpty()) {  
                return result.get(0);  
           }  
           else  
                return null;  
      }  
 }  

Freitag, 30. August 2013

CAS (Central Authentication Service) mit Spring Security - Teil 2

Konfiguration der spring/applicationContextSecurityCas.xml

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:security="http://www.springframework.org/schema/security"
 xmlns:sec="http://www.springframework.org/security/tags"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:util="http://www.springframework.org/schema/util"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/security
  http://www.springframework.org/schema/security/spring-security-3.1.xsd
  http://www.springframework.org/schema/util
  http://www.springframework.org/schema/util/spring-util-2.5.xsd">

 <description>
  This is the main configuration for the security with cas.
 </description>
 
  <!-- Variablen als key/value-Paare in einer Properties-Datei ablegen und dann 
in den Context-Dateien auslesen -->
 <bean id="placeholderConfig" 
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="locations">
  <list>
   <value>/WEB-INF/environment.properties</value>
   <value>/WEB-INF/jdbc.properties</value>
  </list>
  </property>
  <property name="ignoreResourceNotFound" value="false" />
  <property name="ignoreUnresolvablePlaceholders" value="false" />
  <property name="searchSystemEnvironment" value="false" />
 </bean>
  
 
 <security:global-method-security pre-post-annotations="enabled" /> 

 <!-- ======================== Security Filter Chain ======================= -->
 <bean id="springSecurityFilterChain"
  class="org.springframework.security.web.FilterChainProxy">
  <security:filter-chain-map path-type="ant">
   <security:filter-chain pattern="/j_spring_security_logout" 
filters="logoutFilter,etf,filterSecurityInterceptor" />
   <security:filter-chain pattern="/**" filters="
    securityContextPersistentFilter, 
    anonymousAuthenticationFilter, 
    
    casAuthenticationFilter, 
    logoutFilter,
    exceptionTranslationFilterCAS, 
    filterSecurityInterceptor"
   />
 
  </security:filter-chain-map>
 </bean>
 
 <!-- Filter to store the Authentication object in the HTTP Session -->   
 <bean id="securityContextPersistentFilter" 
class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
  <property name="securityContextRepository" ref="securityContextRepository" />
 </bean>
 <bean id="securityContextRepository" 
class="org.springframework.security.web.context.HttpSessionSecurityContextRepository" />

 <!-- anonymousAuthenticationFilter -->
 <bean id="anonymousAuthenticationFilter" 
class="org.springframework.security.web.authentication.AnonymousAuthenticationFilter">
  <property name="key" value="foobar"/>
  <property name="userAttribute" value="anonymousUser, ROLE_ANONYMOUS"/>
 </bean>
 
 <!-- casAuthenticationFilter -->
 <bean id="casAuthenticationFilter" 
class="org.springframework.security.cas.web.CasAuthenticationFilter">
  <property name="authenticationManager" ref="authenticationManager"/>
        <property name="authenticationFailureHandler">
            <bean 
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
                <property name="defaultFailureUrl" value="/casfailed"/>
            </bean>
        </property>
  <property name="authenticationSuccessHandler">
   <bean 
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
    <property name="defaultTargetUrl" value="/"/>
   </bean>
  </property>
 </bean>
    
   <!-- casAuthenticationEntryPoint  -->
   <bean id="casAuthenticationEntryPoint" 
class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
    <!--
    <property name="loginUrl" value="https://sso.your.server.de"/>
    -->
    <property name="loginUrl" value="${sso.host}"/>
    <property name="serviceProperties" ref="casServiceProperties"/>
   </bean>
   
 <!-- exceptionTranslationFilterCAS -->
 <bean id="exceptionTranslationFilterCAS" 
class="org.springframework.security.web.access.ExceptionTranslationFilter">
  <property name="authenticationEntryPoint" ref="casAuthenticationEntryPoint"/>
  <!--
  <property name="accessDeniedHandler" ref="accessDeniedHandler"/>
  -->
 </bean>
 
 <!-- logoutFilter: this filter handles the logout.It should be placed at the 
beginning of the filter chain so a click on the logout link (or button) will not
   go through the rest of the chain -->
    <bean id="logoutFilter" 
class="org.springframework.security.web.authentication.logout.LogoutFilter">
        <!-- 
  <constructor-arg value="/"/>  
  Logout wird noch nicht richtig durchgeführt, 
alternativ https://sso.your.server.de/logout verwenden
  -->  
        <constructor-arg value="/logout"/> 
        <constructor-arg>
            <list>
                <bean 
class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
            </list>
        </constructor-arg>
    </bean>
    
     <bean id="etf" class="org.springframework.security.web.access.ExceptionTranslationFilter">
        <property name="authenticationEntryPoint" ref="preAuthEntryPoint"/>
    </bean>   

 <bean id="preAuthEntryPoint" 
class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint" />

    <bean id="servletContext" 
class="org.springframework.web.context.support.ServletContextFactoryBean"/>

 <!-- Authentication Manager 
 Benutzer gegenüber mehreren Quellen zur Identitätsverwaltung authentifizieren.
 Dies ermöglicht es Spring Security, mehrere Authentifizierungsmechanismen für 
        eine einzelne Anwendung zu unterstützen.
 -->
 <bean id="authenticationManager"
  class="org.springframework.security.authentication.ProviderManager">
  <property name="providers">
   <list>
    <ref local="casAuthenticationProvider"/>
    <!--
    <ref local="authenticationProvider"/>
    <ref local="daoAuthenticationProvider"/>
    -->
    <ref local="anonymousAuthenticationProvider"/>
   </list>
  </property>
 </bean>

 <bean id="dataSource" 
class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName" value="${jdbc.driverClassName}"></property>
  <property name="url" value="${jdbc.databaseurl}"></property>
  <property name="username" value="${jdbc.username}"></property>
  <property name="password" value="${jdbc.password}"></property>
 </bean>
   
 <util:list id="predefinedAdminAccounts" list-class="java.util.ArrayList" 
value-type="java.lang.String">
   <value>adminuser1</value>
   <value>adminuser2</value>
 </util:list>
   
 <bean id="myUserService" class="de.shemel.cas.MyUserService">
  <property name="dataSource" ref="dataSource"/>
  <property name="predefinedAdminAccounts" ref="predefinedAdminAccounts"/>
 </bean>
   
 <bean id="casAuthenticationProvider" 
class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
  <property name="userDetailsService" ref="myUserService" />
  <property name="serviceProperties" ref="casServiceProperties"/>
  <property name="ticketValidator">
   <bean 
class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
    <constructor-arg index="0" value="${sso.host}/" />
    <property name="proxyGrantingTicketStorage">
     <bean 
class="org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl"/>
    </property>
   </bean>
  </property>
  <property name="key" value="tudCasAuthProviderId"/>
 </bean>

 <!-- which service (application) am I authenticating -->
 <bean id="casServiceProperties" class="org.springframework.security.cas.ServiceProperties">
  <!-- 
  <property name="service" 
value="https://your.domain.de/admin/j_spring_cas_security_check"/>
  -->
  <property name="service" value="${host}/admin/j_spring_cas_security_check"/>
  <property name="sendRenew" value="false"/>
 </bean>
 
 <!-- Authentication anonymousAuthenticationProvider -->
 <bean id="anonymousAuthenticationProvider" 
class="org.springframework.security.authentication.AnonymousAuthenticationProvider">
  <property name="key" value="foobar"/>
 </bean>
   
 <bean id="accessDecisionManager" 
class="org.springframework.security.access.vote.AffirmativeBased">
 <property name="allowIfAllAbstainDecisions" value="false"/>
 <property name="decisionVoters">
  <list>
  <bean class="org.springframework.security.access.vote.RoleHierarchyVoter">
     <constructor-arg>
      <bean 
class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
    <property name="hierarchy">
     <value>
      ROLE_ADMIN &gt; ROLE_USER
      ROLE_EMPLOYEE &gt; ROLE_USER
      ROLE_USER &gt; ROLE_ANONYMOUS
     </value>
    </property>
    </bean>
   </constructor-arg>
  </bean>
 <bean class="org.springframework.security.access.vote.RoleVoter"/>
 <bean class="org.springframework.security.access.vote.AuthenticatedVoter"/>
   </list>
  </property>
 </bean>

 <security:filter-security-metadata-source id="securityMetadataSource">
  <security:intercept-url pattern="/**" method="POST"  
access="ROLE_ADMIN, ROLE_USER"/>
  <security:intercept-url pattern="/**" method="GET"  
access="ROLE_ADMIN, ROLE_USER"/>
  <security:intercept-url pattern="/**" method="PUT"  
access="ROLE_ADMIN, ROLE_USER"/>
  <security:intercept-url pattern="/**" method="DELETE"          
access="ROLE_ADMIN, ROLE_USER"/>
  <security:intercept-url pattern="/**"     
access="ROLE_ADMIN, ROLE_USER" />  
  <security:intercept-url pattern="/casfailed"    
access="ROLE_ANONYMOUS" />
  <security:intercept-url pattern="/logout"    
access="ROLE_ANONYMOUS"/>
  <security:intercept-url pattern="/info"    
access="ROLE_ADMIN, ROLE_USER"/>
 </security:filter-security-metadata-source>
 
 <!--=============== Absicherung Webschicht=========================-->
 <!--Filter für die Rechtevergabe auf der Webschicht --> 
 <bean id="filterSecurityInterceptor" 
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
  <property name="authenticationManager" ref="authenticationManager"/>
  <property name="accessDecisionManager" ref="accessDecisionManager"/>
  <property name="securityMetadataSource" ref="securityMetadataSource"/>
 </bean>

</beans>*/
environment.properties
host=http://localhost:8080
#host=your.domain.de
sso.host=https://sso.your.server.de

jdbc.properties
jdbc.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
jdbc.databaseurl=jdbc:sqlserver://your.sql.server.de;instanceName=test;databaseName=test-app
jdbc.username=user
jdbc.password=pw123

In Teil III folgt die Erstellung einer eigenen Java-Klasse für SpringSecurity und CAS (de.shemel.cas.MyUserService) !

Freitag, 9. August 2013

CAS (Central Authentication Service) mit Spring Security - Teil 1

Für eine Authentifikation mit CAS wird ein Centraler Authenticate Server benötigt. Java Servlets übernehmen den Anmelde- und Authentifizierungs-Prozess. In diesem kleinen Beispiel wird die Clientseitige Anbindung an den CAS-Server mit Hilfe von Hibernate und SpringSecurity beschrieben.

Ablauf der Benutzeranmeldung 

Login URL
Bei der Anmeldung wird der Benutzers automatisch auf die Login URL des CAS-Servers weitergeleitet. Hier findet die eigentliche Authentifizierung statt (Benutzername und Passwort). Bei Erfolg, bekommt der Benutzer ein gültiges Ticket.

Validation URL
Mit dem Ticket wird der Benutzer wieder zu der eigentlichen Web-Anwendung zurück geleitet. Das Ticket wird validiert (CAS-Server prüft, ob das Ticket bereits in seiner Datenbank vorhanden ist), der Zugriff auf die Anwendung wird freigegeben.

Logout URL
Die Logout URL ist optional und macht ein Ticket ungültig.

Einrichten von CAS mit Hilfe von Spring Security


Herunterladen der richtigen Libraries unter: http://downloads.jasig.org/cas-clients/?C=M;O=D

Vorbereiten der web.xml
 <context-param>  
      <param-name>contextConfigLocation</param-name>  
      <param-value>  
           /WEB-INF/spring/root-context.xml /WEB-INF/spring/applicationContextSecurityCas.xml  
      </param-value>   
 </context-param>  
...
 <!-- CAS -->  
 <!-- ****************************************************************************************************************************** -->  
 <!-- ProxyTicketReceptor -->       
 <!-- -->  
 <servlet>  
  <servlet-name>ProxyTicketReceptor</servlet-name>  
  <servlet-class>  
      edu.yale.its.tp.cas.proxy.ProxyTicketReceptor  
  </servlet-class>  
 </servlet>  
 <servlet-mapping>  
  <servlet-name>ProxyTicketReceptor</servlet-name>  
  <url-pattern>/CasProxyServlet</url-pattern>  
 </servlet-mapping>   
 <filter>  
   <filter-name>CAS Single Sign Out Filter</filter-name>  
   <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>  
 </filter>  
 <filter>  
           <filter-name>springSecurityFilterChain</filter-name>  
           <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
 </filter>  
 <filter-mapping>  
   <filter-name>CAS Single Sign Out Filter</filter-name>  
   <url-pattern>/*</url-pattern>  
 </filter-mapping>  
 <filter-mapping>  
      <filter-name>springSecurityFilterChain</filter-name>  
      <url-pattern>/*</url-pattern>  
 </filter-mapping>  
 <!-- Default to 60 minute session timeouts -->  
 <session-config>   
      <session-timeout>60</session-timeout>   
 </session-config>  
 <!-- 403 is what the CAS Validation Filter will throw if it has a problem with the ticket -->  
 <error-page>  
      <error-code>403</error-code>  
      <location>/casfailed.jsp</location>  
 </error-page>  
 <!-- ****************************************************************************************************************************** -->  



In Teil II folgt die Konfiguration der spring/applicationContextSecurityCas.xml !