基本的な春のセキュリティ D/B 認証プログラムを実行しようとしています。これを 2 つの方法で試しました。
方法 1 : Spring Security 認証にカスタム テーブルを使用する。
方法 2 : ユーザー認証と承認に Spring セキュリティ固有のデータベース テーブルを使用する。
ファイルの場所:
1. index.jsp -> webapp/index.jsp
2. welcome.jsp -> webapp/pages/welcome.jsp
3. login.jsp -> webapp/pages/login.jsp
方法 1 では、Spring セキュリティはリクエストをインターセプトせず、コンソールにエラーが表示されませんでした。リクエストをインターセプトする代わりに、welcome.jsp に直接移動しました。
PS - 認証を試みていなかったので、セキュリティ コンテキスト xml で以下の「authorities-by-username-query」属性を使用しませんでした。承認用のテーブルも作成する必要があるかどうかはわかりません。
以下は私のsecurity-context.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:jee="http://www.springframework.org/schema/jee"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:tx="http://www.springframework.org/schema/tx"
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/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<security:http auto-config="true">
<security:intercept-url pattern="/welcome.html" />
<security:form-login login-page="/login.html"
default-target-url="/welcome.html" authentication-failure-url="/loginfailed.html" />
<security:logout logout-success-url="/logout.html" />
</security:http>
<security:authentication-manager>
<security:authentication-provider>
<security:jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select FIRST_NAME,LAST_NAME,PASSWORD from USER_AUTHENTICATION where FIRST_NAME=?" />
</security:authentication-provider>
</security:authentication-manager>
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web- app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>SpringPOC</display-name>
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContextDirect.xml
/WEB-INF/applicationContext-security.xml
</param-value>
</context-param>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
BaseController
//@RequestMapping(value="/login", method = RequestMethod.GET)
@RequestMapping("/login")
public ModelAndView login(Model model) {
//System.out.println("Inside /login...");
return new ModelAndView("login");
}
/*public String login(ModelMap model) {
System.out.println("Inside /login...");
return "login";
}*/
@RequestMapping(value="/loginfailed", method = RequestMethod.GET)
public String loginerror(ModelMap model) {
model.addAttribute("error", "true");
return "login";
}
@RequestMapping(value="/logout", method = RequestMethod.GET)
public String logout(ModelMap model) {
return "login";
}
login.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>Login Page</title>
<style>
.errorblock {
color: #ff0000;
background-color: #ffEEEE;
border: 3px solid #ff0000;
padding: 8px;
margin: 16px;
}
</style>
</head>
<body onload='document.f.j_username.focus();'>
<h3>Login with Username and Password (Authentication with Database)</h3>
<c:if test="${not empty error}">
<div class="errorblock">
Your login attempt was not successful, try again.<br /> Caused :
${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}
</div>
</c:if>
<form name='f' action="<c:url value='j_spring_security_check' />"
method='POST'>
<table>
<tr>
<td>User:</td>
<td><input type='text' name='j_username' value=''>
</td>
</tr>
<tr>
<td>Password:</td>
<td><input type='password' name='j_password' />
</td>
</tr>
<tr>
<td colspan='2'><input name="submit" type="submit"
value="submit" />
</td>
</tr>
<tr>
<td colspan='2'><input name="reset" type="reset" />
</td>
</tr>
</table>
</form>
index.jsp
<body>
<div id="content">
<h1>Home Page</h1>
<p>
Anyone can view this page.
</p>
<p><a href="welcome.html">Login page</a></p>
</div>
</body>
方法 2 では、以下のリンクをたどった後、「USERS」および「AUTHORITIES」という名前でスプリング固有のデータベース テーブルを作成しました。ここでは、以下に示すように xml で SQL クエリが使用されていません。
http://www.raistudies.com/spring-security-tutorial/authentication-authorization-spring-security-mysql-database/
security-context.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:jee="http://www.springframework.org/schema/jee"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:tx="http://www.springframework.org/schema/tx"
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/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<security:http realm="Project Realm" auto-config="true">
<security:intercept-url pattern="/welcome.html" access="ROLE_USER"/>
<security:form-login login-page="/login.html"
default-target-url="/welcome.html" authentication-failure-url="/loginfailed.html" />
<security:logout logout-success-url="/logout.html" />
</security:http>
<security:authentication-manager>
<security:authentication-provider>
<security:password-encoder hash="md5"/>
<security:jdbc-user-service data-source-ref="dataSource"/>
</security:authentication-provider>
</security:authentication-manager>
</beans>
上記の方法を試したところ、正しいユーザー名とパスワードを入力しても、「資格情報が正しくありません」というメッセージが表示されました [ただし、この場合、春のセキュリティが要求を傍受しています]。私はOracleデータベースを使用しています。
[更新]:両方の方法でエラーの根本原因を見つけるために、Spring デバッグ ロギングを有効にしました。ログから正確に何が問題なのかを把握または理解できなかったので、両方の方法を試した後に取得したログを比較しました。方法 1 の場合、Spring セキュリティはリクエストをインターセプトせず、方法 2 の場合はログインできました (Spring セキュリティは少なくともリクエストを傍受しています)が、正しいユーザー名とパスワードを入力した後でも、「資格情報が正しくありません」というメッセージが表示されました。
以下は、方法 2 のコード スニペットです [ここでログイン ページを取得しますが、認証に失敗します]
firing Filter: 'FilterSecurityInterceptor'
DEBUG: org.springframework.security.web.util.AntPathRequestMatcher - Checking match of request : '/welcome.html'; against
'/welcome.html'
DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Secure object: FilterInvocation: URL:
/welcome.html; Attributes: [ROLE_USER]
DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Previously Authenticated:
org.springframework.security.authentication.AnonymousAuthenticationToken@9055c2bc: Principal: anonymousUser; Credentials:
[PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364:
RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
DEBUG: org.springframework.security.access.vote.AffirmativeBased - Voter:
org.springframework.security.access.vote.RoleVoter@19432e0, returned: -1
DEBUG: org.springframework.security.access.vote.AffirmativeBased - Voter:
org.springframework.security.access.vote.AuthenticatedVoter@9830bc, returned: 0
DEBUG: org.springframework.security.web.access.ExceptionTranslationFilter - Access is denied (user is anonymous);
redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation
(AbstractSecurityInterceptor.java:206)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke
(FilterSecurityInterceptor.java:115)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter
(FilterSecurityInterceptor.java:84)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter
(AnonymousAuthenticationFilter.java:113)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter
(SecurityContextHolderAwareRequestFilter.java:54)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter
(BasicAuthenticationFilter.java:150)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter
(AbstractAuthenticationProcessingFilter.java:183)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter
(SecurityContextPersistenceFilter.java:87)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:857)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
at java.lang.Thread.run(Thread.java:662)
DEBUG: org.springframework.security.web.savedrequest.HttpSessionRequestCache - DefaultSavedRequest added to Session:
DefaultSavedRequest[http://localhost:8080/itrade-web/welcome.html]
DEBUG: org.springframework.security.web.access.ExceptionTranslationFilter - Calling Authentication entry point.
DEBUG: org.springframework.security.web.DefaultRedirectStrategy - Redirecting to 'http://localhost:8080/itrade-
web/login.html;jsessionid=3FD72892F4F4EF2E65B0C90ABE115354'
DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - SecurityContext is empty or contents
are anonymous - context will not be stored in HttpSession.
DEBUG: org.springframework.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as
request processing completed
DEBUG: org.springframework.security.web.FilterChainProxy - /login.html at position 1 of 10 in additional filter chain;
firing Filter: 'SecurityContextPersistenceFilter'
DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - HttpSession returned null object for SPRING_SECURITY_CONTEXT
firing Filter: 'UsernamePasswordAuthenticationFilter'
...
DEBUG: org.springframework.security.web.FilterChainProxy - /login.html at position 7 of 10 in additional filter chain;
firing Filter: 'AnonymousAuthenticationFilter'
DEBUG: org.springframework.security.web.authentication.AnonymousAuthenticationFilter - Populated SecurityContextHolder with
anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@6fa8940c: Principal:
anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details:
org.springframework.security.web.authentication.WebAuthenticationDetails@fffde5d4: RemoteIpAddress: 0:0:0:0:0:0:0:1;
SessionId: 3FD72892F4F4EF2E65B0C90ABE115354; Granted Authorities: ROLE_ANONYMOUS'
...
DEBUG: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Request is to process
authentication
DEBUG: org.springframework.security.authentication.ProviderManager - Authentication attempt using
org.springframework.security.authentication.dao.DaoAuthenticationProvider
DEBUG: org.springframework.security.provisioning.JdbcUserDetailsManager - Query returned no results for user 'admin'
DEBUG: org.springframework.security.authentication.dao.DaoAuthenticationProvider - User 'admin' not found
DEBUG: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Authentication request
failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials
DEBUG: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Updated SecurityContextHolder
to contain null Authentication
DEBUG: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Delegating to authentication
failure handler org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler@1882c1a
DEBUG: org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler - Redirecting to
/loginfailed.html
DEBUG: org.springframework.security.web.DefaultRedirectStrategy - Redirecting to '/itrade-web/loginfailed.html'
DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - SecurityContext is empty or contents
are anonymous - context will not be stored in HttpSession.
DEBUG: org.springframework.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as
request processing completed
[更新] 方法 1 では、「承認」用のカスタム テーブルを作成した後に、「authorities-by-username-query」タグを追加しました。今、私はログイン画面を取得しているので、春のセキュリティがインターセプトするためには、「authorities-by-username-query」タグが必要であることがわかりました。しかし、ユーザー名とパスワードを入力した後、次のエラーメッセージが表示されます:
Caused : PreparedStatementCallback; uncategorized SQLException for SQL [select FIRST_NAME,LAST_NAME,PASSWORD from USER_AUTHENTICATION where FIRST_NAME=?]; SQL state [null]; error code [17059]; Fail to convert to internal representation; nested exception is java.sql.SQLException: Fail to convert to internal representation
デバッグモードで次の行が表示されます:
DEBUG: org.springframework.security.authentication.ProviderManager - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
INFO : org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [org/springframework/jdbc/support/sql-error-codes.xml]
DEBUG: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Authentication request failed: org.springframework.security.authentication.AuthenticationServiceException: PreparedStatementCallback; uncategorized SQLException for SQL [select FIRST_NAME,LAST_NAME,PASSWORD from USER_AUTHENTICATION where FIRST_NAME=?]; SQL state [null]; error code [17059]; Fail to convert to internal representation; nested exception is java.sql.SQLException: Fail to convert to internal representation
DEBUG: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Updated SecurityContextHolder to contain null Authentication
DEBUG: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Delegating to authentication failure handler org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler@e7736c
DEBUG: org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler - Redirecting to /loginfailed.html
DEBUG: org.springframework.security.web.DefaultRedirectStrategy - Redirecting to '/itrade-web/loginfailed.html'
[更新]: 正しいユーザー名とパスワードを入力しても、両方の方法で同じエラーが発生します。また、D/B からデータを取得できたので、データが存在しないために間違っていないと確信しています。 D/Bで。
DEBUG: org.springframework.security.provisioning.JdbcUserDetailsManager - Query returned no results for user 'user'
このエラーの背後には他の理由があるはずだと思います。
[編集] 今、私は D/B の「users_detail」テーブルをフォローしています:
ユーザー ID 整数
ユーザー名 VARCHAR2 (50 バイト)
パスワード VARCHAR2 (50 バイト)
有効な整数
「users_detail」テーブルのデータ:
USER_ID ユーザー名 パスワード有効
100 ユーザー 123456 1
私のクエリは security-context.xml にあります:
"select username,password, enabled from users_detail where username=?"
クエリを手動で実行するとき、つまりusername = 'user'のusers_detailからusername、password、enabledを選択します。結果セットを取得します。
どこが間違っていますか?D/B に同じエントリがあるにもかかわらず、JdbcUserDetailsManager クラスが常に「クエリはユーザー 'user' の結果を返しませんでした」を返すのはなぜですか。
デバッグ モードでは、上記のエラーが発生したときに JdbcUserDetailsManager クラスのどのメソッドが実行されているかが表示されません。どうすればそれを知ることができますか? また、パスワードフィールドを保存する際に、スプリングは内部的に暗号化/復号化技術を実行しますか?