{"id":53,"date":"2010-01-28T20:45:30","date_gmt":"2010-01-28T19:45:30","guid":{"rendered":"http:\/\/www.pmannel.de\/wordpress\/?p=53"},"modified":"2011-01-08T17:41:19","modified_gmt":"2011-01-08T16:41:19","slug":"gaej-und-spring-teil-3","status":"publish","type":"post","link":"https:\/\/www.pmannel.de\/wordpress\/?p=53","title":{"rendered":"GAE\/J und Spring: Teil 3"},"content":{"rendered":"<p><strong>Spring Security und GAE\/J <\/p>\n<p><\/strong><\/p>\n<p>Da die normale Spring-Security.jar nur semi-compatible ist, laden wir uns eine modifizierte Version hier herunter:<a href=\"http:\/\/www.google-app-engine.com\/blog\/post\/Spring-security-fix-for-google-app-engine.aspx\">Download<\/a><\/p>\n<p>Diese kommt dann statt der <em>spring-security.jar<\/em> in unseren Classpath.<\/p>\n<p>Danach wird die \u201cFilterkette\u201d von Spring in der <em>web.xml<\/em> konfiguriert und die <em>application-security-context.xml<\/em> eingebunden:<\/p>\n<pre class=\"brush:xml\">\r\n\t<context-param>\r\n<param-name>contextConfigLocation<\/param-name>\r\n<param-value>\r\n\t\t\tWEB-INF\/application-context.xml\r\n\t\t\tWEB-INF\/application-security-context.xml\r\n\t\t\t<\/param-value>\r\n\t<\/context-param>\r\n\t<filter>\r\n  \t\t<filter-name>springSecurityFilterChain<\/filter-name>\r\n  \t\t<filter-class>org.springframework.web.filter.DelegatingFilterProxy<\/filter-class>\r\n\t<\/filter>\r\n\r\n\t<filter-mapping>\r\n  \t\t<filter-name>springSecurityFilterChain<\/filter-name>\r\n  \t\t<url-pattern>\/*<\/url-pattern>\r\n\t<\/filter-mapping>\r\n<\/pre>\n<p>Da wir unseren <em>UserDetailsService<\/em>, sowie unsere <em>UserDetails <\/em>und <em>GrantedAuthority <\/em>selber schreiben m\u00fcssen, mu\u00dfte ich ein etwas gr\u00f6\u00dferes Refactoring machen. Ich werde aber, wenn alles fertig ist, die App im SVN \u00f6ffentlich machen, so das sich jeder die Sourcen downloaden kann.<\/p>\n<p>Als n\u00e4chstes wird die application-security-context.xml konfiguriert:<br \/>\n(Diese ist schon f\u00fcr sp\u00e4ter etwas umfangreicher geworden, aber ich denke, man findet sich trotzdem noch gut zurecht)<\/p>\n<pre class=\"brush:xml\">\r\n<beans xmlns=\"http:\/\/www.springframework.org\/schema\/beans\" xmlns:security=\"http:\/\/www.springframework.org\/schema\/security\" xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\" xsi:schemalocation=\"http:\/\/www.springframework.org\/schema\/beans\r\n\r\nhttp:\/\/www.springframework.org\/schema\/beans\/spring-beans-2.0.xsd\r\n\r\nhttp:\/\/www.springframework.org\/schema\/security\r\n\r\n   http:\/\/www.springframework.org\/schema\/security\/spring-security-2.0.4.xsd\"> \r\n\r\n \t<security:http entry-point-ref=\"authenticationEntryPoint\" access-denied-page=\"\/noAccess.html\">\r\n \t\t<security:intercept-url pattern=\"\/login.*\" filters=\"none\">\r\n \t\t<security:intercept-url pattern=\"\/register.*\" filters=\"none\">\r\n \t\t<security:intercept-url pattern=\"\/secure\/**\" access=\"ROLE_USER\">\r\n\r\n \t<\/security:intercept-url>\r\n\r\n \t<security:authentication-provider user-service-ref=\"userService\">\r\n\t\t<security:password-encoder hash=\"sha-256\" ref=\"passwordEncoder\">\r\n\t\t\t<security:salt-source user-property=\"username\">\r\n\t\t<\/security:salt-source>\r\n\t<\/security:password-encoder>\r\n\r\n\t<bean id=\"passwordEncoder\" class=\"org.springframework.security.providers.encoding.ShaPasswordEncoder\">\r\n\r\n \t<bean id=\"userService\" class=\"de.pmannel.security.GAEUserdetailsService\">\r\n<property name=\"userDAO\" ref=\"userDAO\">\r\n \t<\/property>\r\n\r\n \t <bean id=\"authenticationEntryPoint\" class=\"org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint\">\r\n<property name=\"loginFormUrl\" value=\"\/login.html\">\r\n<property name=\"forceHttps\" value=\"true\">\r\n\t<\/property> \r\n\r\n \t<security:authentication-manager alias=\"authenticationManager\">\r\n\r\n  \t<bean id=\"myFilter\" class=\"de.pmannel.security.MyAuthenticationProcessingFilter\">\r\n<property name=\"defaultTargetUrl\" value=\"\/secure\/myapp.html\">\r\n<property name=\"authenticationFailureUrl\" value=\"\/login.html?login_error=1\">\r\n  \t\t<security:custom-filter position=\"AUTHENTICATION_PROCESSING_FILTER\">\r\n<property name=\"authenticationManager\" ref=\"authenticationManager\">\r\n \t<\/property>\r\n<\/security:custom-filter>\r\n<\/property><\/property><\/bean><\/security:authentication-manager><\/property><\/bean><\/bean><\/bean><\/security:authentication-provider><\/security:intercept-url><\/security:intercept-url><\/security:http><\/beans>\r\n<\/pre>\n<p>Als n\u00e4chstes schreiben wir uns unseren UserDetailsService und unsere GrantedAuthority:<\/p>\n<pre class=\"brush:java\">\r\npublic class GAEGrantedAuthority implements GrantedAuthority\r\n{\r\n\r\n\tpublic static final String ROLE_USER \t= \"ROLE_USER\";\r\n\tpublic static final String ROLE_ADMIN \t= \"ROLE_ADMIN\";\r\n\r\n\t@PrimaryKey\r\n\t@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)\r\n\tprivate Key key;\r\n\r\n\t@Persistent\r\n\tprivate String role;\r\n\r\n\t@Override\r\n\tpublic String getAuthority()\r\n\t{\r\n\t\treturn role;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int compareTo(Object o)\r\n\t{\r\n\t\tif (o != null && o instanceof GrantedAuthority)\r\n\t\t{\r\n\t\t\tGrantedAuthority rhs = (GrantedAuthority) o;\r\n\t\t\treturn this.role.compareTo(rhs.getAuthority());\r\n\t\t}\r\n\t\treturn -1;\r\n\t}\r\n\/\/getter\/setter\r\n}\r\n<\/pre>\n<pre class=\"brush:java\">\r\npublic class GAEUserdetailsService implements UserDetailsService\r\n{\r\n\tprivate UserDAO userDAO;\r\n\r\n\t@Override\r\n\tpublic UserDetails loadUserByUsername(String arg0)\r\n\t\t\tthrows UsernameNotFoundException, DataAccessException\r\n\t{\r\n\t\tUserDetails userDetails = userDAO.findUserByUsername(arg0);\r\n\t\tif(null == userDetails)\r\n\t\t\tthrow new UsernameNotFoundException(\"Invalid User Credentials\");\r\n\r\n\t\treturn userDetails;\r\n\t}\r\n\r\n\tpublic void setUserDAO(UserDAO userDAO)\r\n\t{\r\n\t\tthis.userDAO = userDAO;\r\n\t}\r\n\r\n}\r\n\r\n<\/pre>\n<p>Anschlie\u00dfend implementiert der User noch die UserDetails:<\/p>\n<pre class=\"brush:java\">\r\n@PersistenceCapable(identityType = IdentityType.APPLICATION)\r\npublic class User implements UserDetails\r\n{\r\n\t@PrimaryKey\r\n\t@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)\r\n\tprivate Key key;\r\n\r\n\t@Persistent\r\n\tprivate String password;\r\n\r\n\t@Persistent\r\n\tprivate String username;\r\n\r\n\t@Persistent\r\n\tprivate boolean enabled;\r\n\r\n\t@Persistent\r\n\tprivate boolean accountNonLocked;\r\n\r\n\t@Persistent\r\n\tprivate boolean credentialsNonExpired;\r\n\r\n\t@Persistent\r\n\tprivate boolean accountNonExpired;\r\n\r\n\t@Persistent(defaultFetchGroup = \"true\")\r\n\tprivate List<gaegrantedauthority> grantedAuthorities = Lists.newArrayList();\r\n\r\n\t@Persistent\r\n\tprivate String email;\r\n\r\n\t@Persistent\r\n\tprivate BigDecimal weight;\r\n\r\n\t@Persistent\r\n\tprivate BigDecimal desiredWeight;\r\n\r\n\t@Persistent(mappedBy = \"user\",defaultFetchGroup = \"true\")\r\n\tprivate List<weightunit> weightUnits  = Lists.newArrayList();\r\n\r\n\t@Override\r\n\tpublic GrantedAuthority[] getAuthorities()\r\n\t{\r\n\t\treturn (GrantedAuthority[]) grantedAuthorities.toArray(new GrantedAuthority[]{});\r\n\t}\r\n\/\/getter,setter...\r\n}\r\n<\/weightunit><\/gaegrantedauthority>\r\n<\/pre>\n<p>Die in der <em>application-security.xml<\/em> eingetragenen <em>MyAuthenticationProcessingFilter <\/em>und <em>MyAuthenticationEntryPoint <\/em>kann man vorerst leer implementieren und verdrahten \u2026<\/p>\n<p>Nat\u00fcrlich m\u00fcssen jetzt noch Controller und JSPs f\u00fcr den \u201csicheren Bereich\u201d angelegt werden, aber das sollte ja kein Problem sein.<\/p>\n<p>Interessanterweise funktioniert alles, wenn ich die App  auf meinem Rechner deploye, jedoch fliegen in der Cloud noch Exceptions!<br \/>\nDies liegt daran, das <em>Strings <\/em>einen <em>StringTrimmerEditor <\/em>ben\u00f6tigen! Entweder registriert jeder <em>Controller <\/em>seinen eigenen <em>CustomEditor <\/em>oder man registriert global einen <em>Editor <\/em>f\u00fcr alle <em>Strings<\/em>. Ich habe gelesen, das man besser auch s\u00e4mtliche Variablen, die das <em>BackingObject <\/em>im <em>View <\/em>benutzt, lieber nicht als primitive Datentypen anlegt!<\/p>\n<p>Inzwischen l\u00e4uft alles schon testm\u00e4\u00dfig in der Cloud unter <a href=\"http:\/\/weightdroid.appspot.com\/login.html\">Link<\/a><\/p>\n<p>Falls einer Lust &amp;  Zeit hat, Oberfl\u00e4chen zu designen, kann er sich ja mal melden!<\/p>\n<p>Als n\u00e4chstes werde ich die Eingabeoberfl\u00e4chen programmieren (darauf gehe ich aber nicht ein) und ich werde versuchen, irgendewas \u00e4hnlich JFreeChart oder so einzubinden, damit ich Verlaufsgrafiken darstellen kann. Au\u00dferdem versuche ich, Springs <em>MailSender <\/em>einzubinden\u2026<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Spring Security und GAE\/J Da die normale Spring-Security.jar nur semi-compatible ist, laden wir uns eine modifizierte Version hier herunter:Download Diese kommt dann statt der spring-security.jar in unseren Classpath. Danach wird die \u201cFilterkette\u201d von Spring in der web.xml konfiguriert und die application-security-context.xml eingebunden: contextConfigLocation WEB-INF\/application-context.xml WEB-INF\/application-security-context.xml springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy springSecurityFilterChain \/* Da wir unseren UserDetailsService, sowie unsere&#8230; <\/p>\n<div class=\"read-more\"><a href=\"https:\/\/www.pmannel.de\/wordpress\/?p=53\">Weiterlesen<\/a><\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,11],"tags":[],"class_list":["post-53","post","type-post","status-publish","format-standard","hentry","category-google-app-engine","category-spring-security"],"_links":{"self":[{"href":"https:\/\/www.pmannel.de\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/53","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.pmannel.de\/wordpress\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.pmannel.de\/wordpress\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.pmannel.de\/wordpress\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pmannel.de\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=53"}],"version-history":[{"count":7,"href":"https:\/\/www.pmannel.de\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/53\/revisions"}],"predecessor-version":[{"id":134,"href":"https:\/\/www.pmannel.de\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/53\/revisions\/134"}],"wp:attachment":[{"href":"https:\/\/www.pmannel.de\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=53"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.pmannel.de\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=53"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.pmannel.de\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=53"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}