spring security基於數據庫表進行認證


我們從研究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中則比較清晰,用戶,角色,權限。用戶只能去通過角色去間接的綁定權限,而不能直接去與權限綁定。

以上純屬個人見解。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM