91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Spring怎么實現用戶存儲

發布時間:2022-10-18 16:04:32 來源:億速云 閱讀:120 作者:iii 欄目:編程語言

這篇文章主要介紹“Spring怎么實現用戶存儲”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“Spring怎么實現用戶存儲”文章能幫助大家解決問題。

Spring Security

首先,我們看下如何在認證的過程中配置訪問用戶數據的服務。

針對上節演示的案例,我們需要改進的用戶存儲,也就是用戶名、密碼以及其他信息存儲的地方,在進行認證決策的時候,會對其行檢索。而不是寫死的用戶名和隨機的密碼。

Spring Security非常靈活,能夠基于各種數據存儲來認證用戶。它內置了許多常見的用戶存儲場景。如: 內存,關系型數據庫,以及LDAP。
當然,我們也可以編寫并插入自定義的用戶存儲實現。借助Spring Security的Java配置,我們能夠很容易地配置一個或多個數據存儲方案。那我們就從最簡單的開始:在內存中維護用戶存儲。

1 使用基于內存的用戶存儲。

我們在上節的基礎上進行改造,因為我們的安全配置類 SecurityConfig 擴展了 WebSecurityConfigurerAdapter ,因此配置用戶存儲最簡單的方法就是 重載 configure() 方法。

/**
 * @author itguang
 * @create
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("itguang").password("123456").roles("USER").and()
                .withUser("admin").password("123456").roles("ADMIN");
    }
}

我們可以看到,configure方法中的 AuthenticationManagerBuilder 類型的參數,采用了構造者風格風格的接口來構建認證配置。
通過簡單的調用 inMemoryAuthentication() 就能啟用基于內存的的用戶存儲。
通過 withUser 我們添加了兩個基于內存存儲的用戶,并且分別授予的不同的角色。
withUser()方法返回的是 UserDetailsManagerConfigurer.UserDetailsBuilder ,這個對象提供了多個進一步配置用戶的方法。

測試

接下來,就可以啟動項目進行測試了。首先還是訪問: http://localhost/hello ,會自動動跳轉到 http://localhost/login 讓我們去登陸。
這次我們就不用再去啟動日志中找生成的隨機密碼了,我們用 上面配置的兩個用戶分別進行測試,發現都能夠正確登陸,到這里一個簡單的基于內存的用戶存儲就完成了。

UserDetailsManagerConfigurer.UserDetailsBuilder除了password()、roles()和and()方法以外,還有其他的幾個方法可以用來配置內存用戶存儲中的用戶信息

如下表所示:

但是通常來說,我們的系統肯定不止兩個用戶,基于內存的用戶存儲還是不能滿足我們的要求,我們期待最好能將數據保存在某種關系型數據庫中。

沒錯,Spring Security也為我們提供了基于關系型數據庫的 用戶存儲。

2 使用基于數據庫的用戶存儲

用戶數據通常會存儲在關系型數據庫中并通過JDBC進行訪問。為了配置Spring Security配置以JDBC為支撐的用戶存儲。我們可以使用jdbcAuthentication()方法。

所需的最簡單的配置如下所示:

/**
 * @author itguang
 * @create
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{

    @Autowired
    private DataSource dataSource;

    //基于數據庫表的用戶存儲
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws

在這里我們只需要配置一個dataSource即可,這樣就可以訪問關系型數據庫了。

其中 dataSource是通過自動裝配的技巧得到的。在springboot下 dataSource 是自動裝配的。
我們只需要引入如下依賴

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

并在appliciation.properties 中配置spring.datasource 相關屬性即可。

spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=root

Spring Boot就會使用該配置創建一個DataSource。并裝配為Spring的一個bean。因此我們可以直接使用 @Autowired 進行注入。

好了,直到目前為止,對基于數據庫的用戶存儲已經配置完畢,你可能回想,那既然基于數據庫了,肯定要在數據庫創建相應的表了。那表結構又是怎么樣的呢?

不要著急,接下來我們就開始數據庫表結構的創建。

回到我們的 SpringSecurityConfig 的配置類,我們看下 jdbcAuthentication 的返回值是: JdbcUserDetailsManagerConfigurer,我們再看看 JdbcUserDetailsManagerConfigurer
類中還有哪些方法。

我們主要看下黃框標識出的幾個方法,

/**
     * Populates the {@link DataSource} to be used. This is the only required attribute.
     *
     * @param dataSource the {@link DataSource} to be used. Cannot be null.
     * @return
     * @throws
    public JdbcUserDetailsManagerConfigurer<B> dataSource(DataSource dataSource)
            throws Exception {
        this.dataSource = dataSource;
        getUserDetailsService().setDataSource(dataSource);
        return this;
    }

    /**
     * Sets the query to be used for finding a user by their username. For example:
     *
     * <code>
     *     select username,password,enabled from users where username = ?
     * </code>
     * @param query The query to use for selecting the username, password, and if the user
     * is enabled by username. Must contain a single parameter for the username.
     * @return The {@link JdbcUserDetailsManagerConfigurer} used for additional
     * customizations
     * @throws
    public JdbcUserDetailsManagerConfigurer<B> usersByUsernameQuery(String query)
            throws Exception {
        getUserDetailsService().setUsersByUsernameQuery(query);
        return this;
    }   
    /**
     * Sets the query to be used for finding a user's authorities by their username. For
     * example:
     *
     * <code>
     *     select username,authority from authorities where username = ?
     * </code>
     *
     * @param query The query to use for selecting the username, authority by username.
     * Must contain a single parameter for the username.
     * @return The {@link JdbcUserDetailsManagerConfigurer} used for additional
     * customizations
     * @throws
    public JdbcUserDetailsManagerConfigurer<B> authoritiesByUsernameQuery(String query)
            throws Exception {
        getUserDetailsService().setAuthoritiesByUsernameQuery(query);
        return this;
    }
    /**
     * An SQL statement to query user's group authorities given a username. For example:
     *
     * <code>
     *     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
     * </code>
     *
     * @param query The query to use for selecting the authorities by group. Must contain
     * a single parameter for the username.
     * @return The {@link JdbcUserDetailsManagerConfigurer} used for additional
     * customizations
     * @throws
    public JdbcUserDetailsManagerConfigurer<B> groupAuthoritiesByUsername(String query)
            throws Exception {
        JdbcUserDetailsManager userDetailsService = getUserDetailsService();
        userDetailsService.setEnableGroups(true);
        userDetailsService.setGroupAuthoritiesByUsernameQuery(query);
        return this;
    }

通過查看源碼以及方法上的注釋,我們發現這三個方法主要是用來配置 用戶查詢(usersByUsernameQuery),權限查詢(authoritiesByUsernameQuery),
權限組查詢(groupAuthoritiesByUsername)的sql語句的。并且Spring已經給我們配置了默認的實現。

//默認的用戶查詢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";

當然我們完全可以自定義這些語句。就像下面這樣

@Autowired
    private DataSource dataSource;

    //基于數據庫表的用戶存儲
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication()
                .dataSource(dataSource)
                .usersByUsernameQuery("select username,password,enabled,email from users where username=?")
                .authoritiesByUsernameQuery("select username,authority from authorities where username = ?")
                .groupAuthoritiesByUsername("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");


    }

接下來,就是按照我們定義的這些語句進行表結構的創建了。創建完之后,所有表模型如下圖:

表結構創建語句如下:

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

-- ----------------------------
-- Table structure for groups
-- ----------------------------
DROP TABLE IF EXISTS `groups`;
CREATE TABLE `groups` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `group_name` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT

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

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

-- ----------------------------
-- 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,
  `email` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT

測試:

現在,基于數據庫的用戶存儲已經配置完畢,雖然看起來比基于內存的用戶存儲代碼多,但仔細查看源碼,還是不難理解的。

我們重新啟動應用程序,瀏覽器訪問: http://localhost/hello ,不出意外的有跳轉到了 http://localhost/login 讓我們進行登陸。但是現在我們的數據庫中并沒有用戶。

所以我們還需要在數據庫中創建用戶,打開數據庫,在users表中添加一個用戶,如下:

我們填入我們新加入的用戶名和密碼,點擊登錄,就會發現,登陸失敗:

可以看到錯誤信息為: Reason: Bad credentials

這是因為雖然我們只添加了用戶,但是并沒有為此用戶分配權限,在 authoritiesByUsernameQuery 執行sql時查詢到的就是null,就會返回 Bad credentials。

我們只需要在數據庫的 authorities 表中為 itguang 用戶添加一個權限即可。

再次登陸,就會發現已經可以登陸成功。

**對于最后一個 groupAuthoritiesByUsername 根據用戶名查詢用戶組權限的sql,即使查詢為null,只要是用戶已經在 authorities 表中分配了權限,
就可以登陸成功。對于 權限–用戶–角色(用戶組) 的關系,不清楚的,可以去網上搜一下,有很多文章講的很清楚。**

最后再補充一個小知識,看一下上面的認證查詢,它會預期用戶名密碼存儲在了數據庫中。這里唯一的問題就是,如果使用明文存儲密碼,會帶來安全性問題。
但是如果數據庫中的密碼進行轉碼的話,認證就會失敗,因為它與用戶提交的明文密碼并不匹配。

為了解決這個問題,我們需要借助passwordEncoder()方法指定一個密碼轉碼器(encoder)。

@Autowired
    private DataSource dataSource;

    //基于數據庫表的用戶存儲
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication()
                .dataSource(dataSource)
                .usersByUsernameQuery("select username,password,enabled,email from users where username=?")
                .authoritiesByUsernameQuery("select username,authority from authorities where username = ?")
                .groupAuthoritiesByUsername("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")
                .passwordEncoder(new StandardPasswordEncoder("53cr3t"));


    }

passwordEncoder()方法可以接受Spring Security中PasswordEncoder接口的任意實現。
Spring Security的加密模塊包括了三個這樣的實現:BCryptPasswordEncoder、NoOpPasswordEncoder和StandardPasswordEncoder。

上述的代碼中使用了StandardPasswordEncoder,采用的是 “SHA-256”算法加密實現。

但是如果內置的實現無法滿足需求時,你可以提供自定義的實現。

關于“Spring怎么實現用戶存儲”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

从江县| 若羌县| 连州市| 历史| 秀山| 遵化市| 南和县| 喀喇| 平武县| 青岛市| 玉田县| 荔浦县| 桃源县| 河间市| 陆丰市| 达拉特旗| 赤水市| 和田县| 青田县| 始兴县| 乐都县| 曲阳县| 商河县| 桃江县| 茌平县| 南丰县| 南安市| 梁平县| 丰顺县| 隆安县| 东乡县| 陈巴尔虎旗| 紫金县| 西青区| 大足县| 上饶县| 兰坪| 枝江市| 静海县| 彭山县| 锡林郭勒盟|