4

我是 Spring 新手(以及一般的 Web 开发),但我设法创建了一个简单的 Web 应用程序,现在我正在尝试为其添加身份验证支持,以便用户需要登录才能访问其功能.

我正在使用 Spring MVC 3.2.2、Spring Security 3.1.4 和 Tomcat 7。

出于测试目的,我能够使用硬编码的用户添加身份验证支持,并且一切正常。现在,我必须使用现有的 PostgreSQL 数据库来验证用户。我要强调的是,我的应用程序不支持创建用户;用户已经存储在数据库中。

以下是问题:
(1) 应用程序必须针对 PostgreSQL 数据库对用户进行身份验证,我无法以任何方式修改该数据库。
(2) 使用 crypt('plain text password', gen_salt(md5)) 对数据库中的密码进行哈希处理。
(3) 由于我大量使用了注解和xml配置,基本上大部分的辛苦工作都是由Spring完成的,也就是说很多事情都在幕后进行,我不知道。结果,我现在无法设置密码编码器必须用来验证用户身份的盐。这样的盐必须是存储在数据库中的已经散列的密码。

问题是:
如何告诉 Spring Security 使用存储在数据库中的哈希密码作为密码编码器的 salt?有什么方法可以从 xml 执行此操作,还是我需要继续实现某些类?

我在网上进行了广泛的搜索,到目前为止我发现的所有示例都与我所做的有很大的不同,主要是因为在示例中,大多数功能都是由开发人员实现的,而不是主要依赖于内置功能.

这是security.xml文件:

<http use-expressions="true">
    <intercept-url pattern="/resources/images/**" access="permitAll" requires-channel="any"/>
    <intercept-url pattern="/resources/css/**" access="permitAll" requires-channel="any"/>
    <intercept-url pattern="/login*" access="permitAll" requires-channel="any"/>
    <intercept-url pattern="/**" access="hasAnyRole('ROLE_Processer', 'ROLE_Verifier', 'ROLE_Approver', 'ROLE_Supervisor', 'ROLE_Admin')" requires-channel="any"/>

    <session-management>
        <concurrency-control max-sessions="1" expired-url=/login?expired=true" session-registry-alias="sessionRegistry"/>
    </session-management>

    <form-login
        login-page="/login"
        default-target-url="/"
        always-use-default-target="true"
        authentication-failure-url="/login?error=true"/>

    <logout logout-success-url="/login" invalidate-session="true" delete-cookies="JSESSIONID"/>
</http>

<authentication-manager>
    <authentication-provider>
        <jdbc-user-service
            data-source-ref="dataSource"
            users-by-username-query="SELECT id AS username, password, enabled FROM users WHERE id=?;"
            authorities-by-username-query="SELECT id AS username, role FROM users WHERE id=?;"
            role-prefix="ROLE_"/>

        <password-encoder hash="md5">
            <salt-source ???/>
        </password-encoder>
    </authentication-provider>
</authentication-manager>

这是web.xml文件的相关摘录:

<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>

<listener>
    <listener-class>
      org.springframework.security.web.session.HttpSessionEventPublisher
    </listener-class>
</listener>

这是application-context.xml文件的相关摘录:
<beans:bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> <beans:property name="driverClass" value="org.postgresql.Driver"/> <beans:property name="url" value="jdbc:postgresql://host:port/database"/> <beans:property name="username" value="username"/> <beans:property name="password" value="password"/> </beans:bean>

在实现方面,这是唯一相关的控制器LoginController.java(所有其他控制器都处理应用程序提供的实际功能):

@Controller
公共类登录控制器 {

private static final Logger LOGGER = LoggerFactory.getLogger(LoginController.class);

/**
 * Displays the Login form.
 */
@RequestMapping(value="/login", method=RequestMethod.GET)
public String displayLoginForm(@RequestParam(value="error", required=false) String error, @RequestParam(value="expired", required=false) String expired, Model model) {

    LOGGER.info("Displaying Login form:: displayLoginForm(Model)");

    if (error != null) {
        LOGGER.info("Invalid username or password.");
        model.addAttribute("error", "Invalid username or password.");
    }

    if (expired != null) {
        LOGGER.info("Session has been expired (possibly due to multiple concurrent logins being attempted as the same user)");
        model.addAttribute("expired", "Session has been expired (possibly due to multiple concurrent logins being attempted as the same user).");
    }

    return "login";
}}

任何指针将不胜感激。我要提前感谢大家的时间和帮助。

编辑:
我尝试过,
<password-encoder hash="md5"> <salt-source user-property="password"/> </password-encoder>
因为根据我阅读文档的理解,这是存储在数据库中的用户密码。但是,它不起作用。每次我尝试登录时,都会收到无效的凭据消息;但是,凭据确实有效。

也许此页面的哈希和身份验证部分中描述的任何事件都可能是它无法正常工作的原因。我想知道如何按照建议编写适当的测试。

另一个编辑:
作为测试,我注释掉了密码编码器元素并尝试使用纯文本密码进行身份验证(即,我创建了一个测试用户并将纯文本密码存储在数据库中)。有效。因此,问题肯定出在 Spring Security 对用户输入的密码进行编码的方式上,它与存储在数据库中的散列密码不匹配。

4

1 回答 1

3

好吧,为了解决我的问题,我实现了一个自定义密码编码器。基本上,我覆盖了 PasswordEncoder 接口的匹配方法:

@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
    //encoding raw password: the given encodedPassword serves as the salt
    String sql = "SELECT crypt(?, ?) as hashedpwd;";
    String hashedPassword = aJdbcTemplate.query(sql, new String[] {rawPassword.toString(), encodedPassword}, new ResultSetExtractor<String>() {
        @Override
        public String extractData(ResultSet rs) throws SQLException, DataAccessException {
            rs.next();
            return rs.getString("hashedpwd");
        }
    });
    return encodedPassword.equals(hashedPassword.toString());
}
于 2013-08-18T17:13:13.900 回答