专栏名称: Guo_1_9
目录
相关文章推荐
51好读  ›  专栏  ›  Guo_1_9

Spring之旅第十二站:Spring Security 数据存储、拦截请求 、认证用户、*、(Ja...

Guo_1_9  · 掘金  ·  · 2018-02-27 06:51

正文

请到「今天看啥」查看全文


为了让Spring 满足我们应用的需求,还需要在添加一些配置。

  • 配置用户储存
  • 指定哪些请求需要认证,那些请求不需要认证,以及所需要的权限
  • 提供一个自定义的登录页面,替代原来简单的默认登录页。

除了Spring Security的这些功能,我们可能还希望给予安全限制,有选择性在Web视图上显示特定的内容。

4 选择查询用户详细信息的服务。

我们所需要的是用户的存储,也就是用户名、密码以及其他信息存储的地方,在进行认证决策的时候,对其进行检索。

好消息是Spring Security非常灵活,能够给予各种数据库存储来认证用户名,它内置了多种常见的用户存储场景,如内存、关系型数据库,以及LDAP,但我们也可以编写并插入自定义的用户存储实现。

借助于Spring Security的Java配置,我们能够很容易的配置一个或多个数据库存储方案。

5、使用基于内存的用户存储

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .withUser("user").password("password").roles("USER").and()
            .withUser("admin").password("password").roles("USER","ADMIN");
    }

通过简单那的调用 inMemoryAuthentication 就能启用内存用户村苏。但是,我们还需要一些用户,否则的话这个没用户并没有且别。需要调用weithuser为其存储添加新的用户。以及给定用户授予一个或多个角色权限的reles()方法

对于调式和开发人员来讲,基于内存的用户存储是很有用的,但对于生产级别应用来讲,这就不是最理想的状态了。

5、基于数据库进行认证

用户数据通常会存储在关系型数据库中,并通过JDBC进行访问。为了配置Spring Security使用以JDBC为支撑的用户存储,我们可以使用jdbcAuthentication()方法,所需的最少配置。

@Autowired
DataSource dataSource;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.jdbcAuthentication()
			.dataSource(dataSource);
}

我们必须要配置的知识一个DataSource,这样的话就能 访问关系型数据库里。

尽管默认的最少配置能够让一切运转起来,但是,它对我们的数据库模式有一些要求。它预期存在某些存储用户数据的表。

public static final String DEF_USERS_BY_USERNAME_QUERY =
				"select username,password,enabled " +
				"from users " +
				"where username = ?";
public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY =
				"select username,authority " +
				"from authorities " +
				"where username = ?";
public static final String DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY =
				"select g.id, g.group_name, ga.authority " +
				"from groups g, group_members gm, group_authorities ga " +
				"where gm.username = ? " +
				"and g.id = ga.group_id " +
				"and g.id = gm.group_id";

在第一个查询中,我们获取了用户的用户名、密码以及是否启用的信息,这些信息用来进行用户认证。接下来查询查找 了用户所授予的权限,用来进行鉴权。最后一个查询中,查找了用户作为群组的成员所授予的权限。

如果你能够在数据库中定义和填充满足这些查询的表,那么基本上就不需要你在做什么额外的事情了。

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.jdbcAuthentication()
		 .dataSource(dataSource)
		 .usersByUsernameQuery(
			 "select username,password,true" +
			 "from Spitter where username=?")
			.authoritiesByUsernameQuery(
			"select username,'ROLE_USER' from Spitter where username=?");
}

在本例中,我们只重写了认证和基本权限的查询语句,但是通过调用 groupAuthoritiesByUsername() 方法,我们也能够将群组权限重写为自定义的查询语句。

为了解决密码明文的问题,我们借助于passwordEncode()方法指定一个密码转码器(encoder)

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.jdbcAuthentication()
		 .dataSource(dataSource)
		 .usersByUsernameQuery(
			 "select username,password,true" +
			 "from Spitter where username=?")
			.authoritiesByUsernameQuery(
			"select username,'ROLE_USER' from Spitter where username=?")
		.passwordEncoder(new StandardPasswordEncoder("53cd3t"));
}

passwordEncoder()方法可以接受Spring Security中passwordEncoder接口的任意实现。加密模块包含了三个这样的实现

  • StandardPasswordEncoder
  • NoOpPasswordEncoder
  • BCryptPasswordEncoder

上述代码使用了 StandardPasswordEncoder ,但是如果内置的实现无法满足需求时,你可以提供自定义的实现 , passwordEncoder 接口如下:

package org.springframework.security.crypto.password;
/**
 * Service interface for encoding passwords.
 */
public interface PasswordEncoder {
    /**
     * Encode the raw password.
     */
    String encode(CharSequence rawPassword);
    /**
     * Verify the encoded password obtained from storage matches the submitted raw password after it too is encoded.
     */
    boolean matches(CharSequence rawPassword, String encodedPassword);
}

不管使用哪一个密码转化器,都需要理解的一点是:数据库的秘密是永远不会解码的,所采取的策略与之相反。用户在登录时输入的密码会按照相同的算法进行转码,然后在于数据库中已经转码过的密码进行对比,这个对比是在 PasswordEncoder 的matches()方法中进行的。







请到「今天看啥」查看全文