我们从研究org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl.class的源码开始
public class JdbcDaoImpl extends JdbcDaoSupport
implements UserDetailsService, MessageSourceAware {
//默认的用户查询sql
public static final String DEF_USERS_BY_USERNAME_QUERY = "select username,password,enabled "
+ "from users " + "where username = ?";
  //默认的权限查询sql
public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY = "select username,authority "
+ "from authorities " + "where username = ?";
  //默认的权限组查询sql
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"; protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); private String authoritiesByUsernameQuery;
private String groupAuthoritiesByUsernameQuery;
private String usersByUsernameQuery;
  //角色前缀默认为""
private String rolePrefix = "";
private boolean usernameBasedPrimaryKey = true;
private boolean enableAuthorities = true;
  //权限组默认未设置
private boolean enableGroups;   //构造时填充sql
public JdbcDaoImpl() {
this.usersByUsernameQuery = DEF_USERS_BY_USERNAME_QUERY;
this.authoritiesByUsernameQuery = DEF_AUTHORITIES_BY_USERNAME_QUERY;
this.groupAuthoritiesByUsernameQuery = DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY;
} protected MessageSourceAccessor getMessages() {
return this.messages;
} protected void addCustomAuthorities(String username,
List<GrantedAuthority> authorities) {
} public String getUsersByUsernameQuery() {
return this.usersByUsernameQuery;
} @Override
protected void initDao() throws ApplicationContextException {
Assert.isTrue(this.enableAuthorities || this.enableGroups,
"Use of either authorities or groups must be enabled");
}

  //加载用户方法,实现了UserDetailsService接口
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
List<UserDetails> users = loadUsersByUsername(username);//调用方法加载用户 if (users.size() == 0) {
this.logger.debug("Query returned no results for user '" + username + "'"); throw new UsernameNotFoundException(
this.messages.getMessage("JdbcDaoImpl.notFound",
new Object[] { username }, "Username {0} not found"));
} UserDetails user = users.get(0); //get(0)说明如果数据库中有多个相同name的user,那么以第一个为准 Set<GrantedAuthority> dbAuthsSet = new HashSet<GrantedAuthority>(); if (this.enableAuthorities) {
       //加载权限,如果下面的权限组执行了这里的权限将被覆盖,因为最终会被存入dbAuthsSet 这个set集合中
dbAuthsSet.addAll(loadUserAuthorities(user.getUsername()));
} if (this.enableGroups) {
        //加载权限组
dbAuthsSet.addAll(loadGroupAuthorities(user.getUsername()));
} List<GrantedAuthority> dbAuths = new ArrayList<GrantedAuthority>(dbAuthsSet);
    
addCustomAuthorities(user.getUsername(), dbAuths); if (dbAuths.size() == 0) {
this.logger.debug("User '" + username
+ "' has no authorities and will be treated as 'not found'"); throw new UsernameNotFoundException(this.messages.getMessage(
"JdbcDaoImpl.noAuthority", new Object[] { username },
"User {0} has no GrantedAuthority"));
}
    //创建用户
return createUserDetails(username, user, dbAuths);
} protected List<UserDetails> loadUsersByUsername(String username) {
    //spring jdbc去查询user
return getJdbcTemplate().query(this.usersByUsernameQuery,
new String[] { username }, new RowMapper<UserDetails>() {
@Override
public UserDetails mapRow(ResultSet rs, int rowNum)
throws SQLException {
String username = rs.getString(1);//注意:你的sql查询结果顺序
String password = rs.getString(2);//这里和下面都是
boolean enabled = rs.getBoolean(3);
return new User(username, password, enabled, true, true, true,
AuthorityUtils.NO_AUTHORITIES);//除了前三个我们可以操纵,后面的状态值都是固定开启的
} });
}   //注意事项和上面一样
protected List<GrantedAuthority> loadUserAuthorities(String username) {
return getJdbcTemplate().query(this.authoritiesByUsernameQuery,
new String[] { username }, new RowMapper<GrantedAuthority>() {
@Override
public GrantedAuthority mapRow(ResultSet rs, int rowNum)
throws SQLException {
              //查询结果默认会加上角色前缀
String roleName = JdbcDaoImpl.this.rolePrefix + rs.getString(2); return new SimpleGrantedAuthority(roleName);
}
});
}   //同上
protected List<GrantedAuthority> loadGroupAuthorities(String username) {
return getJdbcTemplate().query(this.groupAuthoritiesByUsernameQuery,
new String[] { username }, new RowMapper<GrantedAuthority>() {
@Override
public GrantedAuthority mapRow(ResultSet rs, int rowNum)
throws SQLException {
String roleName = getRolePrefix() + rs.getString(3); return new SimpleGrantedAuthority(roleName);
}
});
} protected UserDetails createUserDetails(String username,
UserDetails userFromUserQuery, List<GrantedAuthority> combinedAuthorities) {
String returnUsername = userFromUserQuery.getUsername(); if (!this.usernameBasedPrimaryKey) {
returnUsername = username;
} return new User(returnUsername, userFromUserQuery.getPassword(),
userFromUserQuery.isEnabled(), true, true, true, combinedAuthorities);
} public void setAuthoritiesByUsernameQuery(String queryString) {
this.authoritiesByUsernameQuery = queryString;
} protected String getAuthoritiesByUsernameQuery() {
return this.authoritiesByUsernameQuery;
} public void setGroupAuthoritiesByUsernameQuery(String queryString) {
this.groupAuthoritiesByUsernameQuery = queryString;
} public void setRolePrefix(String rolePrefix) {
this.rolePrefix = rolePrefix;
} protected String getRolePrefix() {
return this.rolePrefix;
} public void setUsernameBasedPrimaryKey(boolean usernameBasedPrimaryKey) {
this.usernameBasedPrimaryKey = usernameBasedPrimaryKey;
} protected boolean isUsernameBasedPrimaryKey() {
return this.usernameBasedPrimaryKey;
} public void setUsersByUsernameQuery(String usersByUsernameQueryString) {
this.usersByUsernameQuery = usersByUsernameQueryString;
} protected boolean getEnableAuthorities() {
return this.enableAuthorities;
} public void setEnableAuthorities(boolean enableAuthorities) {
this.enableAuthorities = enableAuthorities;
} protected boolean getEnableGroups() {
return this.enableGroups;
} public void setEnableGroups(boolean enableGroups) {
this.enableGroups = enableGroups;
} @Override
public void setMessageSource(MessageSource messageSource) {
Assert.notNull(messageSource, "messageSource cannot be null");
this.messages = new MessageSourceAccessor(messageSource);
}
}

主要几点在我注释的那些地方,可以看出这种方式是很不灵活的一种方式,但足够满足大多数小项目了。

根据这个内置的实现我们的数据表应该使用5张表来满足它,users,authorities,groups以及两张关联表。

基本结构如下:引用http://www.cnblogs.com/tyb1222/p/4155670.html

/*
Navicat MySQL Data Transfer Source Server : localhost
Source Server Version : 50621
Source Host : localhost:3306
Source Database : security Target Server Type : MYSQL
Target Server Version : 50621
File Encoding : 65001 Date: 2014-12-10 15:49:04
*/ SET FOREIGN_KEY_CHECKS=0; -- ----------------------------
-- Table structure for authorities
-- ----------------------------
DROP TABLE IF EXISTS `authorities`;
CREATE TABLE `authorities` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20) DEFAULT NULL,
`authority` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; -- ----------------------------
-- Table structure for groups
-- ----------------------------
DROP TABLE IF EXISTS `groups`;
CREATE TABLE `groups` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`groupName` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; -- ----------------------------
-- Table structure for group_authorities
-- ----------------------------
DROP TABLE IF EXISTS `group_authorities`;
CREATE TABLE `group_authorities` (
`group_Id` int(11) NOT NULL AUTO_INCREMENT,
`authority` varchar(50) DEFAULT NULL,
PRIMARY KEY (`group_Id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; -- ----------------------------
-- Table structure for group_members
-- ----------------------------
DROP TABLE IF EXISTS `group_members`;
CREATE TABLE `group_members` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`userName` varchar(20) DEFAULT NULL,
`group_Id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; -- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` int(8) NOT NULL AUTO_INCREMENT,
`userName` varchar(20) DEFAULT NULL,
`password` varchar(50) DEFAULT NULL,
`enabled` tinyint(4) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

配置文件如下:

@Configuration
@EnableWebSecurity//启用web安全功能
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
private DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
//基于数据库表进行认证,当调用groupAuthoritiesByUsername这个方法时enableGroups将被设置为true
     auth.jdbcAuthentication().dataSource(dataSource).usersByUsernameQuery("select username,password,enabled from users where username=?").groupAuthoritiesByUsername("select g.id, g.groupname, 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");
    //其实只有auth.jdbcAuthentication().dataSource(dataSource)这一句就已经可以了,因为我们写的sql与它内部的sql是一样的。当需要时才去手写它,比如当需要调整查询条件或查询结果时
} }

还有一点是我们数据库表里一定要有用户并且关联至少一个权限,不然认证不会通过的。

这是基于数据库表进行认证最简单的一种方式,并且限制较多,只能处理用户和权限(不能处理角色,虽然可以开启权限组但是没什么用,我们只能判断用户是否拥有权限却不能判断用户是否属于某一组),如果你的项目权限比较复杂那么推荐你去扩展UserDetailsService实现自定义加载数据。

经过这几天研究shiro和springsecurity之后感觉它俩最大的不同就是springsecurity中的角色和权限的概念完全是同一个东西。而shiro中则比较清晰,用户,角色,权限。用户只能去通过角色去间接的绑定权限,而不能直接去与权限绑定。

以上纯属个人见解。

spring security基于数据库表进行认证的更多相关文章

  1. Spring Security 使用数据库用户进行认证

    本文参考或摘录自:http://haohaoxuexi.iteye.com/blog/2157769 本文使用Spring Security自带的方式连接数据库对用户进行认证. 1.Spring Se ...

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

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

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

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

  4. Spring Security技术栈开发企业级认证与授权(一)环境搭建

    本项目是基于慕课网的Spring Security技术栈开发企业级认证与授权,采用IDEA开发,本文章用来记录该项目的学习过程. 慕课网视频:https://coding.imooc.com/clas ...

  5. 为什么要用hibernate 与基于数据库表结构的项目开发

    最近开始学习hibernate,其实并不知道要学习什么,有什么用.后来问了一下同事,他就说快捷方便简单,很多事情不用自己做他会帮你做好,但是我觉得不应该是这样的,于是我就去搜了一下,就搜到了一篇帖子, ...

  6. Spring Boot+Spring Security+JWT 实现 RESTful Api 认证(二)

    Spring Boot+Spring Security+JWT 实现 RESTful Api 认证(二) 摘要 上一篇https://javaymw.com/post/59我们已经实现了基本的登录和t ...

  7. Spring Boot+Spring Security+JWT 实现 RESTful Api 认证(一)

    标题 Spring Boot+Spring Security+JWT 实现 RESTful Api 认证(一) 技术 Spring Boot 2.Spring Security 5.JWT 运行环境 ...

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

    (接上篇) 后台 先说一下AuthConfig.java Spring Security的主要配置文件之一 AuthConfig 1 @Configuration 2 @EnableWebSecuri ...

  9. spring boot:spring security实现oauth2+jwt管理认证授权及oauth2返回结果格式化(spring boot 2.3.3)

    一,为什么oauth2要整合jwt? 1,OAuth2的token技术有一个最大的问题是不携带用户信息,所以资源服务器不能进行本地验证, 以致每次对于资源的访问,资源服务器都需要向认证服务器的toke ...

随机推荐

  1. angularjs指令弹框点击空白处隐藏及常规方法

    效果图展示: 第一种方法:angularjs自定义指令: 指令: app.directive('onBlankHide', function () { return { restrict: 'A', ...

  2. apache shiro学习笔记

    一.权限概述 1.1 认证与授权 认证:系统提供的用于识别用户身份的功能,通常登录功能就是认证功能-----让系统知道你是谁?? 授权:系统授予用户可以访问哪些功能的许可(证书)----让系统知道你能 ...

  3. Android WebView的使用(用来显示网页)

    1.WebView介绍 2.URL介绍 简单说就是网址. 3.java后台代码 package com.lucky.test34webview; import android.support.v7.a ...

  4. paraview添加vector

    https://youtu.be/cygVdhn-kG0 (须自行FQ)

  5. Windows 安装Angular CLI

    1.安装nvm npm cnpm nrm(onenote笔记上有记录) 参考:https://blog.csdn.net/tyro_java/article/details/51232458 提示:如 ...

  6. [转] JavaScript控制浏览器全屏及各种浏览器全屏模式的方法、属性和事件

    [From] http://www.jb51.net/article/76695.htm HTML 5中的full screen,目前可以在除IE和opera外的浏览器中使用 ,有的时候用来做全屏AP ...

  7. Jsch初步

    [From] http://xpenxpen.iteye.com/blog/2061869 上一篇文章我们成功搭建了sshd服务器,并通过3种方式登陆上了ssh.这一篇我们将用开源jar包jsch来登 ...

  8. pandas强化练习

    这篇文章写得更好:http://wittyfans.com/coding/%E5%88%A9%E7%94%A8Pandas%E5%88%86%E6%9E%90%E7%BE%8E%E5%9B%BD%E4 ...

  9. 解决matplotlib绘图中文乱码

    # 指定默认字体 下面三条代码用来解决绘图中出现的乱码 matplotlib.rcParams['font.sans-serif'] = ['SimHei'] matplotlib.rcParams[ ...

  10. 关于i18n

    现在工作主要负责小程序端,很少负责backend.最近的一个任务是配置多语言.因为一开始都是写死的中文,现在需要把那些变成英文. 狂搜了一波,其实网上的方法都不怎好.(可能就是一开始看的时候觉得好.) ...