溫馨提示×

溫馨提示×

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

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

SpringBoot 中Security如何使用

發(fā)布時間:2021-08-03 14:51:45 來源:億速云 閱讀:139 作者:Leah 欄目:編程語言

這篇文章將為大家詳細(xì)講解有關(guān)SpringBoot 中Security如何使用,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。

什么是SpringSecurity

Security是Spring全家桶中一個安全框架,他的擴(kuò)展能力非常的強(qiáng),底層是一條過濾器鏈。通過簡單的配置就可以使用,但通過自己的DIY,可以把每個權(quán)限細(xì)化到每個鏈接上去。

shiro沒有學(xué),但只推薦學(xué)一個安全框架

這里搭建的學(xué)習(xí)項(xiàng)目都是使用SpringBoot

獲取SpringSecurity

你可以在maven官網(wǎng)獲取最新版本

SpringBoot 中Security如何使用

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    <version>2.4.2</version>
</dependency>

開始一個SpringBoot項(xiàng)目

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.pipihao</groupId>
    <artifactId>securitylearn</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>securitylearn</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.21</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

項(xiàng)目配置文件

server:
  port: 8001

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/demo?serverTimezone=Asia/Shanghai
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
  thymeleaf:
    cache: false 
    # 因?yàn)門hymeleaf很多有默認(rèn)配置,所以只關(guān)了這個緩存,方便刷新

數(shù)據(jù)庫文件

數(shù)據(jù)庫版本為 8.0


運(yùn)行項(xiàng)目

SpringBoot 中Security如何使用 SpringBoot 中Security如何使用  

登錄

用戶名:user

密碼:控制臺輸出的這密碼

配置Security

方法一:通過配置文件修改登錄賬號密碼
spring:
  security:
  	user:
  	  name: xx
  	  password: xx
方法二:通過自定義配置SecurityConfig配置類

WebSecurityConfigurerAdapter 類是是Security內(nèi)置提供了一個默認(rèn)身份驗(yàn)證的抽象類,繼承此抽象類實(shí)現(xiàn)configure方法則可以對驗(yàn)證操作實(shí)現(xiàn)DIY。[于官方文檔 6.3 標(biāo)題可見]

UserDetailsService接口:查詢數(shù)據(jù)庫用戶名和密碼過程

  • 創(chuàng)建類繼承UsernamePasswordAuthenticationFilter,重寫三個方法
    *

  • 創(chuàng)建類實(shí)現(xiàn)UserDetailService,編寫查詢數(shù)據(jù)過程,返回User對象,這個User對象是安全框架提供對象。

  • PasswordEncoder: 數(shù)據(jù)加密接口,用于返回User對象里面的密碼加密

方法三:自定義配置類UserDetailsService

定義不驗(yàn)證鏈接

@Override
protected void configure(HttpSecurity http) throws Exception {
    /*
        使用and()方法表示關(guān)閉XML標(biāo)記的Java配置,它允許我們繼續(xù)配置父標(biāo)記。如果您閱讀代碼,它也是有道理的。我想配置授權(quán)請求并配置表單登錄并配置HTTP基本身份驗(yàn)證。
         */
    http
        .authorizeRequests()
        .antMatchers("/","/no").permitAll() //可以直接訪問的路徑
        .anyRequest().authenticated()
        .and()
        .formLogin()
        .loginPage("/login.html") //配置登錄路徑
        .loginProcessingUrl("/doLogin")
        .defaultSuccessUrl("/hallo")
        .permitAll()
        ; //設(shè)置 登錄的網(wǎng)頁

    http.csrf().disable(); //如果注釋了這一行,全部要用_csrf的對象來驗(yàn)證了
}

配置訪問權(quán)限/角色

如果是配置訪問角色則使用是hasRole與hasAnyRole

這里非常建議點(diǎn)一下看一下hasRole的源碼 使用Role的時候,User的權(quán)限列表是需要加ROLE_前綴的

這里直接使用的是hasAnyAuthority,還有一個方法是hasAuthority

前者可以配置多個權(quán)限,而后者只能配置一個權(quán)限

接口只是顯示一個字符串

@GetMapping("test")
public String sayTest(){
    return "Test";
}
SecurityConfig代碼
@Override
protected void configure(HttpSecurity http) throws Exception {
    /*
        使用and()方法表示關(guān)閉XML標(biāo)記的Java配置,它允許我們繼續(xù)配置父標(biāo)記。如果您閱讀代碼,它也是有道理的。我想配置授權(quán)請求并配置表單登錄并配置HTTP基本身份驗(yàn)證。
         */
    http
        .authorizeRequests()
        .antMatchers("/","/no").permitAll() //可以直接訪問的路徑
        .antMatchers("/test").hasAnyAuthority("admin") // 訪問權(quán)限
        .anyRequest().authenticated()
        .and()
        .formLogin()
        .loginPage("/login.html") //配置登錄路徑
        .loginProcessingUrl("/doLogin")
        .defaultSuccessUrl("/hallo")
        .permitAll()
        ; //設(shè)置 登錄的網(wǎng)頁

    http.csrf().disable(); //如果注釋了這一行,全部要用_csrf的對象來驗(yàn)證了
}
UserDetailsImpl代碼
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    if(StringUtils.isEmpty(username)){
        throw new RuntimeException("用戶名不能為空");
    }
    IUser iUser= userMapper.getUserByUsername(username);
    if(iUser == null){
        throw new UsernameNotFoundException("無此用戶");
    }
    /*此處查詢用戶角色*/

    List<GrantedAuthority> grantedAuthorityList =
        AuthorityUtils.createAuthorityList("admin"); // 權(quán)限的列表

    return new User(iUser.getUsername(),bCryptPasswordEncoder.encode(iUser.getPassword()),grantedAuthorityList);
}

自定義403界面

// 在此方法內(nèi)加上一行  protected void configure(HttpSecurity http)
http.exceptionHandling().accessDeniedPage("/unauth.html");

權(quán)限注解

@Secured

判斷是否有角色,這里匹配的角色需要加前綴ROLE_

@GetMapping("update")
@Secured({"ROLE_manager"})
public String update(){
    return "update";
}

使用其功能時需要在application類上開起

@SpringBootApplication
@MapperScan("com.pipihao.securitylearn.mapper")
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecuritylearnApplication {
    public static void main(String[] args) {
        SpringApplication.run(SecuritylearnApplication.class, args);
    }
}

UserDetailsServiceImpl

List<GrantedAuthority> grantedAuthorityList =
    AuthorityUtils.createAuthorityList("admin","ROLE_manager");
@PreAuthorize & @PostAuthorize

此注解即有權(quán)限驗(yàn)證功能,又有角色驗(yàn)證功能

@GetMapping("pre1")
@PreAuthorize("hasAnyRole('ROLE_manager')")
public String prePost1(){
    return "prePost1";
}

@GetMapping("pre2")
@PreAuthorize("hasAnyAuthority('admin')")
public String prePost2(){
    return "prePost2";
}
@SpringBootApplication
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecuritylearnApplication {
    public static void main(String[] args) {
        SpringApplication.run(SecuritylearnApplication.class, args);
    }
}

@PostAuthorize 與@PreAuthorize的區(qū)別就是,Pre會先攔截后執(zhí)行,而PostAuthorize是先執(zhí)行,后攔截

所以我例子中沒有過多的講

@PreFilter & @PostFilter

Pre是過濾上傳的數(shù)據(jù),Post過濾返回的數(shù)據(jù)

@GetMapping("list")
@PostFilter("filterObject.username != 'admin' ")
public List<IUser> list(){
    List<IUser> iUsers = new ArrayList<>();
    iUsers.add(new IUser(1,"admin","123"));
    iUsers.add(new IUser(2,"user","123"));
    return iUsers;
}

// Applicationo類上還是要加上下面這個注解,并設(shè)置屬性值
@EnableGlobalMethodSecurity(prePostEnabled = true)

效果圖

SpringBoot 中Security如何使用

上傳則是同理,通過注解寫好判斷,然后測試即可,注:PreFilter過濾的也只是集合和數(shù)組

用戶注銷

/*配置退出登錄*/
http.logout().logoutUrl("/logout").logoutSuccessUrl("no").permitAll();

登錄后,直接通過瀏覽器,訪問此路徑即可(是的,就是如此)

location.href='/logout';

自動登錄

下面是尚硅谷老師寫的原理圖和執(zhí)行流程

SpringBoot 中Security如何使用

如果是微服務(wù),則把數(shù)據(jù)庫改成redis,把cookie改成jwt生成的token

SpringBoot 中Security如何使用

Security 中的一個類內(nèi)JdbcTokenRepositoryImpl

的常量CREATE_TABLE_SQL

create table persistent_logins (username varchar(64) not null, series varchar(64) primary key, token varchar(64) not null, last_used timestamp not null)

有興趣的可以看看源碼 沒興趣的直接在你使用的數(shù)據(jù)庫內(nèi)執(zhí)行上面這行sql創(chuàng)建一個保存登錄信息的表

SpringBoot 中Security如何使用

JdbcTokenRepositoryImpl 是PersistentTokenRepository實(shí)現(xiàn)類
下面這種寫那么應(yīng)該是多態(tài)了

@Autowired
private DataSource dataSource;

@Bean
public PersistentTokenRepository persistentTokenRepository(){
    JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
    jdbcTokenRepository.setDataSource(dataSource);
    //jdbcTokenRepository.setCreateTableOnStartup(true); 設(shè)置啟動時創(chuàng)建自動登錄表
    return jdbcTokenRepository;
}

SecurityConfig的方法

@Override
protected void configure(HttpSecurity http) throws Exception {
    /*自定義403鏈接*/
    http.exceptionHandling().accessDeniedPage("/unauth.html");
    /*配置退出登錄*/
    http.logout().logoutUrl("/logout").logoutSuccessUrl("/no").permitAll();
    /*
        使用and()方法表示關(guān)閉XML標(biāo)記的Java配置,它允許我們繼續(xù)配置父標(biāo)記。如果您閱讀代碼,它也是有道理的。我想配置授權(quán)請求并配置表單登錄并配置HTTP基本身份驗(yàn)證。
         */
    http
        .authorizeRequests()
        .antMatchers("/","/no").permitAll() //可以直接訪問的路徑
        .antMatchers("/test").hasAnyAuthority("admin")
        .antMatchers("/unauth").hasAnyAuthority("xxx")
        .anyRequest().authenticated()
        .and()
        .formLogin()
        .loginPage("/login.html") //配置登錄路徑
        .loginProcessingUrl("/doLogin")
        .defaultSuccessUrl("/hallo")
        .permitAll()
        
        // -------------------就是下面這坨
        .and()
        .rememberMe().tokenRepository(persistentTokenRepository())
        .tokenValiditySeconds(60) // 自動保存的時間,秒為單位
        .userDetailsService(userDetailsService)
        ; //設(shè)置 登錄的網(wǎng)頁

    http.csrf().disable(); //如果注釋了這一行,全部要用_csrf的對象來驗(yàn)證了
}

下面是登錄界面

<form action="/doLogin" method="POST">
    user:<input type="text" name="username"><br>
    pswd:<input type="text" name="password"><br>
    <!--必須name=remember-me不然,是無法接收到是否自動登錄的信息的-->
    自動登錄 <input type="checkbox" name="remember-me"><br>
    <input type="submit">
</form>

然后在登錄的時候打個勾,就可以自動登錄了

在DB中會出現(xiàn)如下的信息

SpringBoot 中Security如何使用

CSRF指令認(rèn)證

第一步 把下面這一行注釋了就開啟了,也就是說他其實(shí)是默認(rèn)開啟的

如果沒有關(guān)閉,則會NullPointerException

//http.csrf().disable();

Spring Security CSRF 會針對Patch,Post,Put,Delete方法進(jìn)行防護(hù)。(都是一些要更改數(shù)據(jù)的方法)

系統(tǒng)默認(rèn)提供了一個csrfToken對象放在HttpSession中,也就是我們所見到了_csrf對象

此對象可以直接使用

開啟CSRF后,則登錄的時【POST】,也需要驗(yàn)證CSRF,而使用HttpSession則需要使用模板引擎,這里我們使用的是Thymeleaf而非JSP。(大同小異)

注:使用Thymeleaf的時候,類上的Controller注解不能寫成RestController,不然無法生效的

@Controller
public class LoginController {

    @GetMapping("login")
    public String login(){
        return "login";
    }
}
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>登錄</title>
</head>
<body>
<!--沒加th:則不會有隱藏域自動生成-->
<form th:action="'/doLogin'" method="POST">
    user:<input type="text" name="username"><br>
    pswd:<input type="text" name="password"><br>
    <!--必須name=remember-me不然,是無法接收到是否自動登錄的信息的-->
    自動登錄 <input type="checkbox" name="remember-me"><br>
    <input type="submit">
</form>
</body>
</html>

切記,默認(rèn)開了CSRF,則每個表單中應(yīng)當(dāng)手動添加一個隱藏域

當(dāng)Thymeleaf因?yàn)槟闶褂昧藅h,則自動給你生成了。

所以 th:action="'/doLogin'" 這樣寫可以省事

如下圖

SpringBoot 中Security如何使用

關(guān)于SpringBoot 中Security如何使用就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI