Spring Security系列之極速入門與實踐教程

@[TOC](Spring Security系列之極速入門與實踐教程)

1. Spring Security

Spring Security 是 Spring 家族中的一個安全管理框架,應用程序的兩個主要區域是“認證”和“授權”(或者訪問控制)。Spring Security是針對Spring項目的安全框架,也是Spring Boot底層安全模塊默認的技術選型

這兩個主要區域是Spring Security 的兩個目標。

  • “認證”(Authentication),是建立一個他聲明的主體的過程(一
    個“主體”一般是指用戶,設備或一些可以在你的應用程序中執行動
    作的其他系統)。
  • “授權”(Authorization)指確定一個主體是否允許在你的應用程序
    執行一個動作的過程。爲了抵達需要授權的店,主體的身份已經有認
    證過程建立。

2. 實驗環境準備

環境準備:

  • JDK 1.8
  • SpringBoot2.2.1
  • Maven 3.2+
  • 開發工具
    • IntelliJ IDEA
    • smartGit

創建一個SpringBoot Initialize項目,詳情可以參考我之前博客:SpringBoot系列之快速創建項目教程


新建項目後,檢查一下spring-boot-starter-security場景啓動器是否配置成功,不需要寫版本

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

SpringBoot有版本仲裁機制,SpringBoot2.2.1的spring-boot-starter-security依賴的Spring security版本是5.2.1的


3. 日誌級別修改

配置Spring Security日誌級別,默認是info的,可以修改爲debug

## logback配置
logging:
  level:
    org:
      springframework:
        security: info

4. 配置用戶名/密碼

隨便寫個接口,訪問時候,就會跳到如下圖的登錄頁面,爲什麼?我們只是引入maven配置而已,然後賬號密碼是什麼?其實這個是Spring Security的默認登錄頁面,頁面代碼是在jar包裏的,默認的username是user,密碼是隨機生成的uuid格式的密碼



密碼會在控制檯打印,根據線索,找到自動配置類



要修改默認密碼,可以新建application.yml配置文件,加上如下配置
## spring security配置
spring:
  security:
    user:
      name: nicky
      password: 123

也可以新建Spring Security配置類,注意Spring Security5.2.1版本,配置密碼要用BCryptPasswordEncoder加密,不過登錄還是明文,Spring Security不同版本各有差別,詳情配置還是參考官方文檔

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {    //auth.inMemoryAuthentication()
        auth.inMemoryAuthentication()
                .withUser("nicky")
                .password(bcryptPasswordEncoder().encode("123"))
                .roles("admin");
    }
    
    @Bean
    public PasswordEncoder bcryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
加密方式 security 4 security 5
bcrypt password {bcrypt}password
ldap password {ldap}password
MD4 password {MD4}password
MD5 password {MD5}password
noop password {noop}password
pbkdf2 password {pbkdf2}password
scrypt password {scrypt}password
SHA-1 password {SHA-1}password
SHA-256 password {SHA-256}password
sha256 password {sha256}password

5. 數據庫方式校驗

拓展:如果要數據庫方式校驗用戶名密碼,可以自定義UserDetailsService方式:

@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {   
        auth.userDetailsService(userDetailsService)
                .passwordEncoder(new CustomPasswordEncoder());
        auth.parentAuthenticationManager(authenticationManagerBean());

    }

UserDetailsServiceImpl.java

package com.example.springboot.oauth2.service;

import com.example.springboot.oauth2.entity.User;
import com.example.springboot.oauth2.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;

/**
 * <pre>
 *
 * </pre>
 *
 * <pre>
 * @author mazq
 * 修改記錄
 *    修改後版本:     修改人:  修改日期: 2020/04/30 15:15  修改內容:
 * </pre>
 */
@Slf4j
@Service("userService")
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    UserMapper userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);
        if(user == null){
            log.info("登錄用戶[{}]沒註冊!",username);
            throw new UsernameNotFoundException("登錄用戶["+username + "]沒註冊!");
        }
        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), getAuthority());
    }

    private List getAuthority() {
        return Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN"));
//        return Arrays.asList(Collections.emptyList());
    }
}

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http   // 配置登錄頁並允許訪問
                .formLogin().usernameParameter("username").passwordParameter("password").loginPage("/login").permitAll()
                // 配置Basic登錄
                //.and().httpBasic()
                // 配置登出頁面
                .and().logout().logoutUrl("/logout").logoutSuccessUrl("/")
                // 開放接口訪問權限,不需要登錄授權就可以訪問
                .and().authorizeRequests().antMatchers("/oauth/**", "/login/**", "/logout/**").permitAll()
                // api接口需要admin管理員才能訪問
                .antMatchers("/api/**").hasRole("admin")
                // 其餘所有請求全部需要鑑權認證
                .anyRequest().authenticated()
                // 關閉跨域保護;
                .and().csrf().disable();
    }

6. 不攔截靜態資源

配置文件,加上配置

@Override
    public void configure(WebSecurity web) throws Exception {
        //解決靜態資源被攔截的問題
        web.ignoring().antMatchers("/asserts/**");
        web.ignoring().antMatchers("/favicon.ico");
    }

7. 自定義登錄頁面

引入Thymeleaf模板引擎:

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

關閉Thymeleaf模板引擎緩存,方便F9自動編譯

spring:
  thymeleaf:
    cache: false

寫個login接口,注意一定要GET方式,POST方式是Spring Security默認的校驗接口,接口名稱也是/login

@Controller
public class LoginController {

    @GetMapping(value = {"/login"})
    public ModelAndView toLogin() {
        return new ModelAndView("login");
    }
}

自定義登錄頁面,要用post方式,除非你自己寫個校驗接口,POST /login是Spring Security官方的校驗接口,默認用戶名參數爲username,密碼參數爲password:

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
        <meta name="description" content="" />
        <meta name="author" content="" />
        <title>Signin Template for Bootstrap</title>
        <!-- Bootstrap core CSS -->
        <link href="../static/asserts/css/bootstrap.min.css" th:href="@{asserts/css/bootstrap.min.css}" rel="stylesheet" />
        <!-- Custom styles for this template -->
        <link href="../static/asserts/css/signin.css" th:href="@{asserts/css/signin.css}" rel="stylesheet"/>
    </head>

    <body class="text-center">
        <form class="form-signin" th:action="@{/login}" method="post">
            <img class="mb-4" th:src="@{asserts/img/bootstrap-solid.svg}" alt="" width="72" height="72" />
            <h1 class="h3 mb-3 font-weight-normal" >Oauth2.0 Login</h1>
            <label class="sr-only" >Username</label>
            <input type="text" class="form-control" name="username" required="" autofocus="" value="nicky" />
            <label class="sr-only" >Password</label>
            <input type="password" class="form-control" name="password" required="" value="123" />
            <div class="checkbox mb-3">
                <label>
          <input type="checkbox" value="remember-me"  /> remember me
        </label>
            </div>
            <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
            <p class="mt-5 mb-3 text-muted">© 2019</p>
        </form>

    </body>

</html>

修改配置文件,.loginPage("/login")指定自定義的登錄頁面

 @Override
    protected void configure(HttpSecurity http) throws Exception {
        http   // 配置登錄頁並允許訪問
                .formLogin().usernameParameter("username").passwordParameter("password").loginPage("/login").permitAll()
                // 配置Basic登錄
                //.and().httpBasic()
                // 配置登出頁面
                .and().logout().logoutUrl("/logout").logoutSuccessUrl("/")
                // 開放接口訪問權限,不需要登錄授權就可以訪問
                .and().authorizeRequests().antMatchers("/oauth/**", "/login/**", "/logout/**").permitAll()
                // api接口需要admin管理員才能訪問
                .antMatchers("/api/**").hasRole("admin")
                // 其餘所有請求全部需要鑑權認證
                .anyRequest().authenticated()
                // 關閉跨域保護;
                .and().csrf().disable();
    }

8. Remember me

開啓記住我功能,登陸成功以後,將cookie發給瀏覽器保存,以後訪問頁面帶上這個cookie,只要通過檢查就可以免登錄

@Override
    protected void configure(HttpSecurity http) throws Exception {
      //開啓記住我功能,登陸成功以後,將cookie發給瀏覽器保存,以後訪問頁面帶上這個cookie,只要通過檢查就可以免登錄
        http.rememberMe().rememberMeParameter("remeber");

    }

ok,Spring Security的知識點比較多,詳情請參考官方文檔,本博客參考官方文檔,做了簡單記錄,僅僅作爲入門參考手冊

代碼例子下載:code download

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章