カスタマイズされた UserDetailsService で spring-security を使用して、フォームベースのログインに取り組んでいます。ログインフォームは正しく送信されているようです。アプリケーションをデバッグしているときに、送信されたリクエストが UsernamePasswordAuthenticationFilter の試行認証メソッドに到着したことがわかりました。しかし、空のままであるため、要求パラメーターをユーザー名とパスワードのフィールドにマップできないようです。したがって、カスタム UserDetailsService の loadByUsername メソッドのパラメータ username も空のままで、正常にログインできません。
私は多くのことを試しましたが、今のところ、何が問題なのかわかりません。私は春のセキュリティにもまったく慣れていませんが、それほど遠くないと思います。
コードを可能な限りまとめました。さらに情報が必要な場合は、今すぐお知らせください。
まず、web.xml で springSecurityFilterChain をセットアップします。
<welcome-file-list>
<welcome-file>index.xhtml</welcome-file>
</welcome-file-list>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:WEB-INF/applicationContext.xml,
classpath:WEB-INF/applicationContext-security.xml
</param-value>
</context-param>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:log4j.properties</param-value>
</context-param>
<!-- Filter Config -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<!-- Filter Mappings -->
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<!-- Spring Configuration -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
これは、customUserDetailsService への参照を含む私の applicationContext.xml です。
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
...
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
...
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
...
</bean>
<!-- Authentification -->
<bean id="customUserDetailsService" class="com.seraphim.security.auth.CustomUserDetailsService"/>
<!-- Transaction -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"/>
<context:component-scan base-package="com.seraphim"/>
<context:annotation-config/>
<tx:annotation-driven transaction-manager="transactionManager"/>
適切な認証プロバイダーを含む applicationContext-security.xml:
<http
auto-config="true"
access-denied-page="/accessDenied.jsp">
<intercept-url
pattern="/pages/**"
access="ROLE_ADMIN,ROLE_USER"/>
<intercept-url
pattern="/**"
access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<form-login
login-processing-url="/j_spring_security_check"
login-page="/index.xhtml"
default-target-url="/pages/main.xhtml"
authentication-failure-url="/index.xhtml"/>
<logout
logout-url="/logout*"
logout-success-url="/"/>
</http>
<authentication-manager>
<authentication-provider user-service-ref="customUserDetailsService">
<password-encoder hash="md5"/>
</authentication-provider>
</authentication-manager>
faces-config.xml に、BadCredentialsException を検出する LoginErrorPhaseListener を追加し、LoginBean を追加しました。
<lifecycle>
<phase-listener>com.seraphim.security.auth.LoginErrorPhaseListener</phase-listener>
</lifecycle>
<managed-bean>
<managed-bean-name>loginBean</managed-bean-name>
<managed-bean-class>
com.seraphim.bean.LoginBean
</managed-bean-class>
<managed-bean-scope>
request
</managed-bean-scope>
</managed-bean>
index.xhtml にはユーザー名とパスワード フィールドがあり、loginBean に送信されます。
<h:form id="loginForm">
<h:outputLabel for="j_username" value="User:"/>
<p:inputText id='j_username' label="user" required="true"/>
<h:outputLabel for="j_password" value="Password:"/>
<h:inputSecret id='j_password' label="pass2" required="true"/>
<h:outputLabel for="_spring_security_remember_me" value="Remember "/>
<p:selectBooleanCheckbox id='_spring_security_remember_me'/>
<h:outputLabel/>
<h:commandButton type="submit" id="login" action="#{loginBean.doLogin}" value="Login"/>
</h:form>
LoginBean.java
@Component
@Scope("request")
public class LoginBean {
public String doLogin() throws IOException, ServletException {
ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
RequestDispatcher dispatcher = ((ServletRequest) context.getRequest())
.getRequestDispatcher("/j_spring_security_check");
dispatcher.forward((ServletRequest) context.getRequest(), (ServletResponse) context.getResponse());
FacesContext.getCurrentInstance().responseComplete();
// It's OK to return null here because Faces is just going to exit.
return null;
}
}
CustomUserDetailsService.java で正しいユーザーをロードする必要がありますが、ここではメソッド パラメーターのユーザー名は常に空です (null ではありません)。したがって、もちろん有効なユーザーは見つかりません。
public class CustomUserDetailsService implements UserDetailsService {
@Resource
IUserDao userDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDao.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("user not found");
}
// build roles for user
Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new GrantedAuthorityImpl("ROLE_USER"));
if(user.isAdmin()) {
authorities.add(new GrantedAuthorityImpl("ROLE_ADMIN"));
}
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
user.isEnabled(),
user.isAccountNonExpired(),
user.isCredentialsNonExpired(),
user.isAccountNonLocked(),
authorities);
}
}
これで私を助けてくれることを願っています。