前面做了多个示例,包括使用jdbc和hibernate两种方式访问数据库获取用户信息和权限信息,其中一些关键步骤如下:
 
我们在SecurityConfig中配置覆盖configure方法时候,可以指定authenticationProvider,也可以不需要指定,直接指定userDetailsService。例如:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
authenticationProvider.setPasswordEncoder(passwordEncoder());
auth.authenticationProvider(authenticationProvider);
//auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
如果没有指定authenticationProvider,则security使用的是实现类DaoAuthenticationProvider。
如果指定自定义的authenticationProvider,为了方便,我们自定义的authenticationProvider也是继承自DaoAuthenticationProvider,只需要重写指定userDetailsService,authenticate方法,例如:
@Autowired
@Qualifier("userDetailsService")
@Override
public void setUserDetailsService(UserDetailsService userDetailsService) {
super.setUserDetailsService(userDetailsService);
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
try {
//调用上层验证逻辑
Authentication auth = super.authenticate(authentication);
//如果验证通过登录成功则重置尝试次数, 否则抛出异常
userDetailsDao.resetFailAttempts(authentication.getName());
return auth;
} catch (BadCredentialsException e) {
//如果验证不通过,则更新尝试次数,当超过次数以后抛出账号锁定异常
userDetailsDao.updateFailAttempts(authentication.getName());
throw e;
} catch (LockedException e){
//该用户已经被锁定,则进入这个异常
String error;
UserAttempts userAttempts =
userDetailsDao.getUserAttempts(authentication.getName());
if(userAttempts != null){
Date lastAttempts = userAttempts.getLastModified();
error = "用户已经被锁定,用户名 : "
+ authentication.getName() + "最后尝试登陆时间 : " + lastAttempts;
}else{
error = e.getMessage();
}
throw new LockedException(error);
}
}

在此方法中,仍然调用的是上层验证方法super.authenticate();在这里可以根据不同的验证异常抛出不同的异常,从而显示不同的用户账号状态,例如用户被锁定、用户失效、账号或者密码过期等,这里例子是多次登录失败锁定了用户。
 
下面我们看看security是如何验证账号的:
验证逻辑实现是在类AbstractUserDetailsAuthenticationProvider,此类实现了接口AuthenticationProvider的接口方法
Authentication authenticate(Authentication authentication)  throws AuthenticationException;

实现方法中首先获取security定义的接口UserDetails,先从缓存userCache中获取,如果不存在,则调用方法retrieveUser。

retrieveUser的方法实现是在类DaoAuthenticationProvider,这个方法中可以看到
UserDetails loadedUser;
try {
loadedUser = this.getUserDetailsService().loadUserByUsername(username);
}
catch (UsernameNotFoundException notFound) {
if (authentication.getCredentials() != null) {
String presentedPassword = authentication.getCredentials().toString();
passwordEncoder.isPasswordValid(userNotFoundEncodedPassword,
presentedPassword, null);
}
throw notFound;
}
.....

此处调用的是自定义的UserDetailsService中loadUserByUsername方法。于是可以看出自定义的UserDetailsService实现类关键是实现loadUserByUsername方法。

 
下面就两种方式的实现进行剖解:
1、使用jdbc方式时,我们自定义的UserDetailsService是继承了类JdbcDaoImpl,可以发现JdbcDaoImpl已经实现了接口UserDetailsService,实现了方法loadUserByUsername。
在实现方法中,关键是调用自己定义的两个方法loadUsersByUsername和createUserDetails。于是自定义的CustomUserDetailsService类只需要覆写这两个方法即可
@Override
protected List<UserDetails> loadUsersByUsername(String username) {
return getJdbcTemplate().query(super.getUsersByUsernameQuery(), new Object[]{username},
(rs, rowNum) -> {
String username1 = rs.getString("username");
String password = rs.getString("password");
boolean enabled = rs.getBoolean("enabled");
boolean accountNonExpired = rs.getBoolean("accountNonExpired");
boolean credentialsNonExpired = rs.getBoolean("credentialsNonExpired");
boolean accountNonLocked = rs.getBoolean("accountNonLocked");
return new User(username1, password, enabled, accountNonExpired, credentialsNonExpired,
accountNonLocked, AuthorityUtils.NO_AUTHORITIES);
});
}
@Override
protected UserDetails createUserDetails(String username, UserDetails userFromUserQuery, List<GrantedAuthority> combinedAuthorities) {
String returnUsername = userFromUserQuery.getUsername();
if (!super.isUsernameBasedPrimaryKey()) {
returnUsername = username;
}
return new User(returnUsername, userFromUserQuery.getPassword(),
userFromUserQuery.isEnabled(),
userFromUserQuery.isAccountNonExpired(),
userFromUserQuery.isCredentialsNonExpired(),
userFromUserQuery.isAccountNonLocked(), combinedAuthorities);
}

在这里我们根据需要指定各个值,例如用户名,密码,是否可用,账号和密码是否过期,是否账号被锁等,所以如果已经设计完成的数据表中字段名称不一致也没有关系,只要含义相同,获取值指定即可。

2、使用hibernate方式时,需要自己实现UserDetailsService接口中的方法loadUserByUsername:
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDao.findByUserName(username);
if (user == null) {
throw new UsernameNotFoundException("该用户不存在:" + username);
}
List<GrantedAuthority> authorities = buildUserAuthority(user.getUserRole());
return buildUserForAuthentication(user, authorities);
}
// 把自定义的User转换成org.springframework.security.core.userdetails.User
private org.springframework.security.core.userdetails.User buildUserForAuthentication(
User user,
List<GrantedAuthority> authorities) {
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(),
user.isEnabled(), user.isAccountNonExpired(), user.isCredentialsNonExpired(), user.isAccountNonLocked(), authorities);
}
private List<GrantedAuthority> buildUserAuthority(Set<UserRole> userRoles) {
Set<GrantedAuthority> setAuths = new HashSet<>();
// Build user's authorities
for (UserRole userRole : userRoles) {
setAuths.add(new SimpleGrantedAuthority(userRole.getRole()));
}
return new ArrayList<>(setAuths);
}

在方法中使用hibernate的方式获取自定义的User实例,然后转换成security中的org.springframework.security.core.userdetails.User即可,org.springframework.security.core.userdetails.User是接口UserDetails的实现类。

 
附上所有示例的代码的github地址: https://github.com/hongxf1990/spring-security-learning 
 
嘿嘿,如果觉得以上实例项目中可以借鉴的话,不妨打个赏吧
                     

 
 

spring security使用数据库验证的逻辑处理的更多相关文章

  1. spring security结合数据库验证用户-XML配置方式

    之前的用户信息我们都是使用的内存用户,测试例子可以,实际中使用肯定不行,需要结合数据库进行验证用户.这就是本节的重点: 项目目录如下:  在之前的项目中的依赖中添加两个依赖: <dependen ...

  2. spring security结合数据库验证用户-注解方式

    项目目录结构如下: 首先数据库的建立和数据导入,以及一些类的依赖参考XML配置方式,需要修改一些配置. 一.在AppConfig文件中添加DataSource的配置 @Bean(name = &quo ...

  3. spring security关闭http验证 和 springboot 使用h2数据库

    spring security关闭http验证 最近在跑demo的过程中,访问swagger页面的时候需要验证登录,记得在之前写的代码中是关闭了security验证,无需登录成功访问,直接在appli ...

  4. Spring Security 概念基础 验证流程

    Spring Security 概念基础 验证流程 认证&授权 认证:确定是否为合法用户 授权:分配角色权限(分配角色,分配资源) 认证管理器(Authentication Manager) ...

  5. 自定义Spring Security的身份验证失败处理

    1.概述 在本快速教程中,我们将演示如何在Spring Boot应用程序中自定义Spring Security的身份验证失败处理.目标是使用表单登录方法对用户进行身份验证. 2.认证和授权(Authe ...

  6. spring boot系列--spring security (基于数据库)登录和权限控制

    先说一下AuthConfig.java Spring Security的主要配置文件之一 AuthConfig 1 @Configuration 2 @EnableWebSecurity 3 publ ...

  7. Spring Security在登录验证中增加额外数据(如验证码)

    在使用Spring Security框架过程中,经常会有这样的需求,即在登录验证时,附带增加额外的数据,如验证码.用户类型等.下面将介绍如何实现. 注:我的工程是在Spring Boot框架基础上的, ...

  8. Spring Security使用数据库数据完成认证--练气后期2

    写在前面 没错,这篇文章还是练气后期!但作者我相信筑基指日可待! 在前一篇文章当中,我们简单地分析了一下Spring Security的认证流程,知道了如果想要实现对自己用户数据(账户.角色.权限)的 ...

  9. spring security使用数据库资源

    国内对权限系统的基本要求是将用户权限和被保护资源都放在数据库里进行管理,在这点上Spring Security并没有给出官方的解决方案,为此我们需要对Spring Security进行扩展.. 数据库 ...

随机推荐

  1. nginx已经启动 无法访问页面

    通过IP访问,可以看到  welcome nginx 的提示 下面我重启linux服务器,重启后通过ip访问,死活连接不上了?没办法了,只有在百度和google 最后发现问题不是出在nginx上,而是 ...

  2. Null value was assigned to a property of primitive type setter

    org.springframework.orm.jpa.JpaSystemException: Null value was assigned to a property of primitive t ...

  3. 《JavaScript高级程序设计》读书笔记1--HTTP头部信息

    优秀的书籍常读常新 ----------------------------------------- 在发送XHR请求的同时,还会发送如下头部信息: --Accept: 浏览器能够处理的内容类型 - ...

  4. 160809、tomcat中配置多个域名及将tomcat配置成系统服务

    本地测试用的(注意红色部分) 第一步.自己的windows电脑,在c盘中有个hosts文件(搜索一下),做以下修改(其中127.0.0.1是本机地址,192.1638.10.139是我虚拟机中linu ...

  5. SQLSERVER 创建索引实现代码

    是SQL Server编排数据的内部方法.它为SQL Server提供一种方法来编排查询数据 什么是索引 拿汉语字典的目录页(索引)打比方:正如汉语字典中的汉字按页存放一样,SQL Server中的数 ...

  6. mysql出现) Notice: Trying to get property of non-object in E:\p错误的 原因

    在mysql中,每个命令之间都要留一点空格 如果是这样, $query = "select * from books where " .$searchtype. "lik ...

  7. android问题总结

    1.当打开eclipse时出现如下窗口(内容如下) Error when loading the SDK: Error: Error parsing \Android\adt-bundle-windo ...

  8. 原!tomcat启动超时(打印了几行日志,后面没了。也不报错,处于启动状态,直到超时)

    项目框架:spring+struts2+mybatis 今天优化代码,改了一堆mybatis dao和xml文件,启动项目时,就出现如标题描述的状况:打印了几行日志,后面就不打印了,也不报错,处于启动 ...

  9. 单独使用celery

    单独使用celery 参考 http://docs.celeryproject.org/en/latest/getting-started/index.html https://www.jianshu ...

  10. IIS设置文件 Robots.txt 禁止爬虫

    robots.txt用于禁止网络爬虫访问网站指定目录.robots.txt的格式采用面向行的语法:空行.注释行(以#打头).规则行.规则行的格式为:Field: value.常见的规则行:User-A ...