spring webflow 2 と spring ldap によるユーザー認証でログインサーブレットを実現しました。これまでのところ、すべて問題ありません。
今、ログインフローに春のセキュリティを導入しようとしています。そのため、Spring Web フロー リファレンス ガイド バージョン 2.4.0 (セクション 8) と Spring セキュリティ LDAP セクション ガイドに従って、構成を調整してフローを保護します。特に、ROLE_USERS ユーザーのみの成功ログイン ページを保護しようとしています。
私が試みたのは、ldap データベースからユーザー名、パスワード、およびユーザーの役割を抽出し、これらの情報を使用してフローにセキュリティを適用することです。そのため、プロジェクトに次の変更を加えます。
loginflow.xml のdisplayLoginSuccessViewビューステートにsecure属性を追加
<?xml version="1.0" encoding="UTF-8"?> <flow xmlns="http://www.springframework.org/schema/webflow" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.4.xsd"> <var name="loginCredentials" class="com.folkture.login.LoginCredentials"/> <view-state id="displayLoginView" view="/WEB-INF/views/display_login.jsp" model="loginCredentials"> <transition on="loginCredentialsEntered" to="performLoginAction"/> </view-state> <action-state id="performLoginAction"> <evaluate expression="loginService.performLogin(loginCredentials)"/> <transition to="displayLoginSuccessView"/> <transition on-exception="com.folkture.login.IncorrectLoginCredentialsException" to="displayLoginErrorView"/> </action-state> <view-state id="displayLoginSuccessView" view="/WEB-INF/views/display_login_success.jsp"> <secured attributes="ROLE_USER" /> </view-state> <view-state id="displayLoginErrorView" view="/WEB-INF/views/display_login_error.jsp"/> </flow>
Bean SecurityFlowExecutionListenerをwebflow-config.xmlに追加します
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:webflow="http://www.springframework.org/schema/webflow-config" xsi:schemaLocation="http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.4.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"> <webflow:flow-executor id="loginFlowExecutor" flow-registry="loginFlowRegistry"> <webflow:flow-execution-listeners> <webflow:listener ref="sfel" /> </webflow:flow-execution-listeners> </webflow:flow-executor> <webflow:flow-registry id="loginFlowRegistry"> <!-- Define the flow executor responsible for executing login web flow --> <webflow:flow-location id="loginFlow" path="/WEB-INF/flows/login-flow.xml" /> </webflow:flow-registry> <!-- Installs a listener to apply Spring Security authorities --> <bean id="sfel" class="org.springframework.webflow.security.SecurityFlowExecutionListener" />
springSecurityFilterChainをweb.xmlに追加します
spring-config.xmlに春のセキュリティ構成を追加する
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:ldap="http://www.springframework.org/schema/ldap" xmlns:security="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd"> <context:property-placeholder location="classpath:/ldap.properties" system-properties-mode="OVERRIDE" /> <context:annotation-config /> <ldap:context-source id="contextSource" url="${sample.ldap.url}" base="${sample.ldap.base}" authentication-source-ref="springSecurityAuthenticationSource" /> <ldap:ldap-template id="ldapTemplate" context-source-ref="contextSource" /> <bean id="loginService" class="com.folkture.login.LoginService"> <property name="ldapTemplate" ref="ldapTemplate" /> </bean> <bean id="springSecurityAuthenticationSource" class="org.springframework.security.ldap.authentication.SpringSecurityAuthenticationSource" /> <security:http auto-config="true"> <security:intercept-url pattern="/**" access="ROLE_USER" /> <security:form-login login-page="/views/display_login.jsp" /> </security:http> <security:authentication-manager> <security:ldap-authentication-provider user-search-filter="(cn={0})" user-search-base="ou=users" group-search-filter="(member={0})" group-search-base="ou=Groups" group-role-attribute="cn" /> </security:authentication-manager> <security:ldap-server url="ldap://localhost:389" manager-dn="${sample.ldap.userDn}" manager-password="${sample.ldap.password}" />
そして、これはログインメソッドLoginService.javaを持つ私のJavaクラスです
package com.folkture.login; import javax.naming.directory.DirContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.ldap.NamingException; import org.springframework.ldap.core.AuthenticatedLdapEntryContextCallback; import org.springframework.ldap.core.DirContextOperations; import org.springframework.ldap.core.LdapEntryIdentification; import org.springframework.ldap.core.LdapTemplate; import org.springframework.ldap.filter.AndFilter; import org.springframework.ldap.filter.EqualsFilter; import org.springframework.security.core.Authentication; import org.springframework.security.ldap.authentication.LdapAuthenticator; import org.springframework.stereotype.Service; @Service public class LoginService implements LdapAuthenticator{ @Autowired private LdapTemplate ldapTemplate; public LoginService() { super(); } public void setLdapTemplate(LdapTemplate ldapTemplate) { this.ldapTemplate = ldapTemplate; System.out.println("setLdapTemplate "+ ldapTemplate); } public String performLogin(LoginCredentials loginCredentials) throws Exception{ if(login(loginCredentials.getLoginName(),loginCredentials.getPassword())) { System.out.println("autenticato!"); return "success"; } else { throw new IncorrectLoginCredentialsException(); } } public boolean login(String username, String password) throws Exception{ AndFilter filter = new AndFilter(); filter.and(new EqualsFilter("objectclass", "inetOrgPerson")).and(new EqualsFilter("cn", username)); if(ldapTemplate.authenticate("ou=users", filter.toString(), password, contextCallback)) return true; return false; } AuthenticatedLdapEntryContextCallback contextCallback = new AuthenticatedLdapEntryContextCallback() { @SuppressWarnings("deprecation") public void executeWithContext(DirContext ctx, LdapEntryIdentification ldapEntryIdentification) { try { try { ctx.lookup(ldapEntryIdentification.getRelativeDn()); } catch (javax.naming.NamingException e) { e.printStackTrace(); } } catch (NamingException e) { throw new RuntimeException("Failed to lookup " + ldapEntryIdentification.getRelativeDn(), e); } } }; @Override public DirContextOperations authenticate(Authentication authentication) { // TODO Auto-generated method stub return null; } }
ユーザー名とパスワードを入力すると、次のエラーが表示されます。
HTTP Status 500 - Request processing failed; nested exception is org.springframework.webflow.execution.FlowExecutionException: Exception thrown in state 'performLoginAction' of flow 'loginFlow'
________________________________________
type Exception report
message Request processing failed; nested exception is org.springframework.webflow.execution.FlowExecutionException: Exception thrown in state 'performLoginAction' of flow 'loginFlow'
description The server encountered an internal error that prevented it from fulfilling this request.
exception
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.webflow.execution.FlowExecutionException: Exception thrown in state 'performLoginAction' of flow 'loginFlow'
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:973)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
root cause
org.springframework.webflow.execution.FlowExecutionException: Exception thrown in state 'performLoginAction' of flow 'loginFlow'
org.springframework.webflow.engine.impl.FlowExecutionImpl.wrap(FlowExecutionImpl.java:573)
org.springframework.webflow.engine.impl.FlowExecutionImpl.resume(FlowExecutionImpl.java:263)
org.springframework.webflow.executor.FlowExecutorImpl.resumeExecution(FlowExecutorImpl.java:169)
org.springframework.webflow.mvc.servlet.FlowHandlerAdapter.handle(FlowHandlerAdapter.java:228)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:938)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:870)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
root cause
java.lang.NullPointerException
org.springframework.security.access.vote.RoleVoter.extractAuthorities(RoleVoter.java:115)
org.springframework.security.access.vote.RoleVoter.vote(RoleVoter.java:96)
org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:62)
org.springframework.webflow.security.SecurityFlowExecutionListener.decide(SecurityFlowExecutionListener.java:109)
org.springframework.webflow.security.SecurityFlowExecutionListener.stateEntering(SecurityFlowExecutionListener.java:74)
org.springframework.webflow.engine.impl.FlowExecutionListeners.fireStateEntering(FlowExecutionListeners.java:144)
org.springframework.webflow.engine.impl.FlowExecutionImpl.setCurrentState(FlowExecutionImpl.java:373)
org.springframework.webflow.engine.impl.RequestControlContextImpl.setCurrentState(RequestControlContextImpl.java:189)
org.springframework.webflow.engine.State.enter(State.java:191)
org.springframework.webflow.engine.Transition.execute(Transition.java:228)
org.springframework.webflow.engine.impl.FlowExecutionImpl.execute(FlowExecutionImpl.java:395)
org.springframework.webflow.engine.impl.RequestControlContextImpl.execute(RequestControlContextImpl.java:214)
org.springframework.webflow.engine.TransitionableState.handleEvent(TransitionableState.java:116)
org.springframework.webflow.engine.Flow.handleEvent(Flow.java:547)
org.springframework.webflow.engine.impl.FlowExecutionImpl.handleEvent(FlowExecutionImpl.java:390)
org.springframework.webflow.engine.impl.RequestControlContextImpl.handleEvent(RequestControlContextImpl.java:210)
org.springframework.webflow.engine.ActionState.doEnter(ActionState.java:105)
org.springframework.webflow.engine.State.enter(State.java:194)
org.springframework.webflow.engine.Transition.execute(Transition.java:228)
org.springframework.webflow.engine.impl.FlowExecutionImpl.execute(FlowExecutionImpl.java:395)
org.springframework.webflow.engine.impl.RequestControlContextImpl.execute(RequestControlContextImpl.java:214)
org.springframework.webflow.engine.TransitionableState.handleEvent(TransitionableState.java:116)
org.springframework.webflow.engine.Flow.handleEvent(Flow.java:547)
org.springframework.webflow.engine.impl.FlowExecutionImpl.handleEvent(FlowExecutionImpl.java:390)
org.springframework.webflow.engine.impl.RequestControlContextImpl.handleEvent(RequestControlContextImpl.java:210)
org.springframework.webflow.engine.ViewState.handleEvent(ViewState.java:231)
org.springframework.webflow.engine.ViewState.resume(ViewState.java:195)
org.springframework.webflow.engine.Flow.resume(Flow.java:537)
org.springframework.webflow.engine.impl.FlowExecutionImpl.resume(FlowExecutionImpl.java:259)
org.springframework.webflow.executor.FlowExecutorImpl.resumeExecution(FlowExecutorImpl.java:169)
org.springframework.webflow.mvc.servlet.FlowHandlerAdapter.handle(FlowHandlerAdapter.java:228)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:938)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:870)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
spring-config.xml の認証プロバイダーの設定が、認証と認可の Java クラスと適切に一致していないと思います。LDAP からロールを正常に取得するにはどうすればよいですか?