溫馨提示×

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

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

Spring Security 安全框架的原理是什么

發(fā)布時(shí)間:2021-07-30 17:51:00 來源:億速云 閱讀:134 作者:Leah 欄目:開發(fā)技術(shù)

本篇文章給大家分享的是有關(guān)Spring Security 安全框架的原理是什么,小編覺得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

認(rèn)證授權(quán)分析

用戶在進(jìn)行資源訪問時(shí),要求系統(tǒng)要對(duì)用戶進(jìn)行權(quán)限控制,其具體流程如圖所示:

Spring Security 安全框架的原理是什么

Spring Security 概述

Spring Security 是一個(gè)企業(yè)級(jí)安全框架,由spring官方推出,它對(duì)軟件系統(tǒng)中的認(rèn)證,授權(quán),加密等功能進(jìn)行封裝,并在springboot技術(shù)推出以后,配置方面做了很大的簡(jiǎn)化.市場(chǎng)上現(xiàn)在的分布式架構(gòu)下的安全控制正在逐步的轉(zhuǎn)向Spring Security.

Spring Security 基本架構(gòu)

Spring Security 在企業(yè)中實(shí)現(xiàn)認(rèn)證和授權(quán)業(yè)務(wù)時(shí),底層構(gòu)建了大量的過濾器.
Spring Security 安全框架的原理是什么
其中:
綠色部分為認(rèn)證過濾器,需要我們自己配置,也可以配置過個(gè)認(rèn)證過濾器.也可以使用Spring Security提供的默認(rèn)認(rèn)證過濾器.黃色部分為授權(quán)過濾器.Spring Security就是通過這些過濾器然后調(diào)用相關(guān)對(duì)象一起完成認(rèn)證和授權(quán)操作.

Spring Security 快速入門

創(chuàng)建工程

Spring Security 安全框架的原理是什么

添加項(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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.3.2.RELEASE</version>
    </parent>

    <groupId>com.cy</groupId>
    <artifactId>02-jt-spring-security</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</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-test</artifactId>
        </dependency>
    </dependencies>
</project>

創(chuàng)建配置文件

在resources目錄下創(chuàng)建application.yml文件,并指定服務(wù)端口

server:
   port: 8080

創(chuàng)建項(xiàng)目啟動(dòng)類

package com.cy.jt;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringSecurityApplication {
    public static void main(String[] args) {
        SpringApplication.run(
                SpringSecurityApplication.class,
                args);
    }
}

運(yùn)行啟動(dòng)類訪問測(cè)試

第一步:檢查控制輸出,是否自動(dòng)生成了一個(gè)密碼,例如:

Using generated security password: 360123aa-df93-4cd9-bab4-5212af421d2c

第二步:打開瀏覽器輸入http://localhost:8080,然后呈現(xiàn)登錄頁(yè)面,例如:

Spring Security 安全框架的原理是什么

在登錄窗口中輸入用戶名user(系統(tǒng)默認(rèn)),密碼(服務(wù)啟動(dòng)時(shí),控制臺(tái)默認(rèn)輸出的
密碼),然后點(diǎn)擊Sign in進(jìn)行登錄,登錄成功默認(rèn)會(huì)出現(xiàn),如下界面:

Spring Security 安全框架的原理是什么

定義登錄成功頁(yè)面

在項(xiàng)目的resources目錄下創(chuàng)建static目錄,并在此目錄創(chuàng)建一個(gè)index.html文件,例如:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
  <h2>Login Ok</h2>
</body>
</html>

啟動(dòng)服務(wù),再次進(jìn)行登錄訪問測(cè)試,登錄成功以后系統(tǒng)默認(rèn)會(huì)跳轉(zhuǎn)到index.html頁(yè)面,例如

Spring Security 安全框架的原理是什么

配置登錄密碼

第一步:編寫一個(gè)方法(可以在啟動(dòng)類中調(diào)用執(zhí)行),對(duì)一個(gè)名文進(jìn)行加密,例如:

static void encodePwd(){
        BCryptPasswordEncoder encoder=new BCryptPasswordEncoder();
        String password="123456";//明文
        String newPwd=encoder.encode("123456");
        System.out.println(newPwd);//$2a$10$fahHJIe3SJm3KcyiPPQ2d.a2qR029gB3qKHrKanQ87u.KbtZ6Phr.
    }

第二步:將用戶和密碼在在springboot工程的application.yml文件中進(jìn)行配置,例如:

spring:
  security:
    user:
      name: jack
      #password: 123456 #這種寫法,密碼太簡(jiǎn)單了
      password: '{bcrypt}$2a$10$fahHJIe3SJm3KcyiPPQ2d.a2qR029gB3qKHrKanQ87u.KbtZ6Phr.'

其中,{bcrypt}指定了密碼加密時(shí)使用的算法

第三步:啟動(dòng)服務(wù),重新進(jìn)行登錄測(cè)試.

SpringSecurity 認(rèn)證邏輯實(shí)現(xiàn)

自定義登陸邏輯

SpringSecurity支持通過配置文件的方式定義用戶信息(賬號(hào)密碼和角色等),但這種方式有明顯的缺點(diǎn),那就是系統(tǒng)上線后,用戶信息的變更比較麻煩。因此SpringSecurity還支持通過實(shí)現(xiàn)UserDetailsService接口的方式來提供用戶認(rèn)證授權(quán)信息,其應(yīng)用過程如下:
第一步:定義security配置類,例如:

/**
 * 由@Configuration注解描述的類為spring中的配置類,配置類會(huì)在spring
 * 工程啟動(dòng)時(shí)優(yōu)先加載,在配置類中通常會(huì)對(duì)第三方資源進(jìn)行初始配置.
 */
@Configuration
public class SecurityConfig {
    /**
     * 定義SpringSecurity密碼加密對(duì)象
     * @Bean 注解通常會(huì)在@Configuration注解描述的類中描述方法,
     * 用于告訴spring框架這個(gè)方法的返回值會(huì)交給spring管理,并spring
     * 管理的這個(gè)對(duì)象起個(gè)默認(rèn)的名字,這個(gè)名字與方法名相同,當(dāng)然也可以通過
     * @Bean注解起名字
     */
    @Bean //對(duì)象名默認(rèn)為方法名
    //@Bean("bcryptPasswordEncoder")//bean對(duì)象名字為bcryptPasswordEncoder
    public BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

第二步:定義UserDetailService接口實(shí)現(xiàn)類,自定義登陸邏輯,代碼如下:
UserDetailService為SpringSecurity官方提供的登錄邏輯處理對(duì)象,我們自己可以實(shí)現(xiàn)此接口,然后在對(duì)應(yīng)的方法中進(jìn)行登錄邏輯的編寫即可.

package com.cy.jt.security.service;
@Service
public class UserDetailServiceImpl implements UserDetailsService {
    @Autowired
    private BCryptPasswordEncoder passwordEncoder;
    /**
     * 當(dāng)我們執(zhí)行登錄操作時(shí),底層會(huì)通過過濾器等對(duì)象,調(diào)用這個(gè)方法.
     * @param username 這個(gè)參數(shù)為頁(yè)面輸出的用戶名
     * @return 一般是從數(shù)據(jù)庫(kù)基于用戶名查詢到的用戶信息
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
        //1.基于用戶名從數(shù)據(jù)庫(kù)查詢用戶信息
        //User user=userMapper.selectUserByUsername(username);
        if(!"jack".equals(username))//假設(shè)這是從數(shù)據(jù)庫(kù)查詢的信息
            throw new UsernameNotFoundException("user not exists");
        //2.將用戶信息封裝到UserDetails對(duì)象中并返回
        //假設(shè)這個(gè)密碼是從數(shù)據(jù)庫(kù)查詢出來的
        String encodedPwd=passwordEncoder.encode("123456");
        //假設(shè)這個(gè)權(quán)限信息也是從數(shù)據(jù)庫(kù)查詢到的
        //假如分配權(quán)限的方式是角色,編寫字符串時(shí)用"ROLE_"做前綴
        List<GrantedAuthority> grantedAuthorities =
                AuthorityUtils.commaSeparatedStringToAuthorityList(
                "ROLE_admin,ROLE_normal,sys:res:retrieve,sys:res:create");
        //這個(gè)user是SpringSecurity提供的UserDetails接口的實(shí)現(xiàn),用于封裝用戶信息
        //后續(xù)我們也可以基于需要自己構(gòu)建UserDetails接口的實(shí)現(xiàn)
        User user=new User(username,encodedPwd,grantedAuthorities);
        return user;
    }
}

說明,這里的User對(duì)象會(huì)交給SpringSecurity框架,框架提取出密碼信息,然后與用戶輸入的密碼進(jìn)行匹配校驗(yàn).

第三步:?jiǎn)?dòng)服務(wù)進(jìn)行登陸,訪問測(cè)試。

Spring Security 安全框架的原理是什么

Spring Security 安全框架的原理是什么

自定義登陸頁(yè)面

第一步:定義登陸頁(yè)面(直接在static目錄下創(chuàng)建即可),關(guān)鍵代碼如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta 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>Please sign in</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>
<body>
<div class="container">
    <form class="form-signin" method="post" action="/login">
        <h3 class="form-signin-heading">Please sign in</h3>
        <p>
            <label for="username" class="sr-only">Username</label>
            <input type="text" id="username" name="username" class="form-control" placeholder="Username" required autofocus>
        </p>
        <p>
            <label for="password" class="sr-only">Password</label>
            <input type="password" id="password" name="password" class="form-control" placeholder="Password" required>
        </p>
        <input name="_csrf" type="hidden" value="cc1471a5-3246-43ff-bef7-31d714273899" />
        <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
    </form>
</div>
</body>
</html>

注意:請(qǐng)求的url暫時(shí)為”/login”,請(qǐng)求方式必須為post方式,請(qǐng)求的參數(shù)暫時(shí)必須為username,password。這些規(guī)則默認(rèn)在UsernamePasswordAuthenticationFilter中進(jìn)行了定義。

第二步:修改安全配置類,讓其實(shí)現(xiàn)接口,并重寫相關(guān)config方法,進(jìn)行登陸設(shè)計(jì),代碼如下:

@Configuration
public class SecutiryConfig extends WebSecurityConfigurerAdapter {
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //super.configure(http);
        //關(guān)閉跨域攻擊,不關(guān)閉容易出錯(cuò)
        http.csrf().disable();
        //自定義登陸表單
        http.formLogin()
                //設(shè)置登陸頁(yè)面
                .loginPage("/login.html")
                //設(shè)置登陸請(qǐng)求處理地址(對(duì)應(yīng)form表單中的action),登陸時(shí)會(huì)訪問UserDetailService對(duì)象
                .loginProcessingUrl("/login")
                //設(shè)置請(qǐng)求用戶名參數(shù)為username(默認(rèn)就是username,可以自己修改,需要與表單同步)
                .usernameParameter("username")
                //請(qǐng)求請(qǐng)求密碼參數(shù)為password(默認(rèn)就是password,可以自己修改,需要與表單同步)
                .passwordParameter("password")
                //設(shè)置登陸成功跳轉(zhuǎn)頁(yè)面(默認(rèn)為/index.html)
                .defaultSuccessUrl("/index.html")
                //登陸失敗訪問的頁(yè)面(默認(rèn)為/login.html?error)
                .failureUrl("/login.html?error");
        //認(rèn)證設(shè)計(jì)
        http.authorizeRequests()
                //設(shè)置要放行的咨詢
                .antMatchers("/login.html").permitAll()
                //設(shè)置需要認(rèn)證的請(qǐng)求(除了上面的要放行,其它都要進(jìn)行認(rèn)證)
                .anyRequest().authenticated();
    }
}

登陸成功和失敗處理器

現(xiàn)在的很多系統(tǒng)都采用的是前后端分離設(shè)計(jì),我們登陸成功以后可能會(huì)跳轉(zhuǎn)到前端系統(tǒng)的某個(gè)地址,或者返回一個(gè)json數(shù)據(jù),我們可以自己定義登錄成功的處理操作,例如:

定義登陸成功處理器:

方案1:可以直接執(zhí)行重定向的處理器,例如

package com.cy.jt.auth.config.authentication;
public class RedirectAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
//定義要跳轉(zhuǎn)的url
    private String redirectUrl;
    public RedirectAuthenticationSuccessHandler(String redirectUrl){
        this.redirectUrl=redirectUrl;
    }
    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse,
                            Authentication authentication)
            throws IOException, ServletException {
            httpServletResponse.sendRedirect(redirectUrl);
    }
}

方案2:可以直接返回JSON數(shù)據(jù)的處理器,例如:

package com.cy.jt.security.config.handler;
/**處理登錄失敗
 * 0)Default-默認(rèn)
 * 1)Authentication-認(rèn)證
 * 2)Failure-失敗
 * 3)Handler-處理器
 * */
public class DefaultAuthenticationFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(
            HttpServletRequest httpServletRequest,
            HttpServletResponse httpServletResponse,
            AuthenticationException e) throws IOException, ServletException {
        //1.設(shè)置響應(yīng)數(shù)據(jù)的編碼
        httpServletResponse.setCharacterEncoding("utf-8");
        //2.告訴客戶端響應(yīng)數(shù)據(jù)的類型,以及客戶端以怎樣的編碼進(jìn)行顯示
        httpServletResponse.setContentType("application/json;charset=utf-8");
        //3.獲取一個(gè)輸出流對(duì)象
        PrintWriter out=httpServletResponse.getWriter();
        //4.向客戶端輸出一個(gè)json格式字符串
        //4.1構(gòu)建一個(gè)map對(duì)象
        Map<String,Object> map=new HashMap<>();
        map.put("state","500");
        map.put("msg","username or password error");
        //4.2基于jackson中的ObjectMapper對(duì)象將一個(gè)對(duì)象轉(zhuǎn)換為json格式字符串
        String jsonStr= new ObjectMapper().writeValueAsString(map);
        out.println(jsonStr);
        out.flush();
    }
}

定義登陸失敗處理器:

方案1:登陸失敗重定向到頁(yè)面,例如

package com.cy.jt.auth.config.authentication;
public class RedirectAuthenticationFailureSuccessHandler implements AuthenticationFailureHandler {
    private String redirectUrl;
    public RedirectAuthenticationFailureSuccessHandler(String redirectUrl){
        this.redirectUrl=redirectUrl;
    }
    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        httpServletResponse.sendRedirect(redirectUrl);
    }
}

方案2:定義登陸失敗處理器,例如:

package com.cy.jt.security.config.handler;

/**處理登錄失敗
 * 0)Default-默認(rèn)
 * 1)Authentication-認(rèn)證
 * 2)Failure-失敗
 * 3)Handler-處理器
 * */
public class DefaultAuthenticationFailureHandler
         implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(
            HttpServletRequest httpServletRequest,
            HttpServletResponse httpServletResponse,
            AuthenticationException e) throws IOException, ServletException {
        //1.設(shè)置響應(yīng)數(shù)據(jù)的編碼
        httpServletResponse.setCharacterEncoding("utf-8");
        //2.告訴客戶端響應(yīng)數(shù)據(jù)的類型,以及客戶端以怎樣的編碼進(jìn)行顯示
        httpServletResponse.setContentType("application/json;charset=utf-8");
        //3.獲取一個(gè)輸出流對(duì)象
        PrintWriter out=httpServletResponse.getWriter();
        //4.向客戶端輸出一個(gè)json格式字符串
        //4.1構(gòu)建一個(gè)map對(duì)象
        Map<String,Object> map=new HashMap<>();
        map.put("state","500");
        map.put("msg","username or password error");
        //4.2基于jackson中的ObjectMapper對(duì)象將一個(gè)對(duì)象轉(zhuǎn)換為json格式字符串
        String jsonStr= new ObjectMapper().writeValueAsString(map);
        out.println(jsonStr);
        out.flush();
    }
}

修改配置類,設(shè)置登陸成功與失敗處理器。

@Configuration
public class SecutiryConfig extends WebSecurityConfigurerAdapter {
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //super.configure(http);
        //關(guān)閉跨域攻擊,不關(guān)閉容易出錯(cuò)
        http.csrf().disable();
        //自定義登陸表單
        http.formLogin()
                //設(shè)置登陸頁(yè)面
                .loginPage("/login.html")
                //設(shè)置登陸請(qǐng)求處理地址(對(duì)應(yīng)form表單中的action),登陸時(shí)會(huì)訪問UserDetailService對(duì)象
                .loginProcessingUrl("/login")
                //設(shè)置請(qǐng)求用戶名參數(shù)為username(默認(rèn)就是username,可以自己修改,需要與表單同步)
                .usernameParameter("username")
                //請(qǐng)求請(qǐng)求密碼參數(shù)為password(默認(rèn)就是password,可以自己修改,需要與表單同步)
                .passwordParameter("password")
                //設(shè)置登陸成功跳轉(zhuǎn)頁(yè)面(默認(rèn)為/index.html)
                .successHandler(new RedirectAuthenticationSuccessHandler("你的url"))
                //登陸失敗訪問的頁(yè)面(默認(rèn)為/login.html?error)
               .failureHandler(new RedirectAuthenticationFailureHandler("你的url"))
        //認(rèn)證設(shè)計(jì)
                http.authorizeRequests()
                //設(shè)置要放行的咨詢
                .antMatchers("/login.html").permitAll()
                //設(shè)置需要認(rèn)證的請(qǐng)求(除了上面的要放行,其它都要進(jìn)行認(rèn)證)
               .anyRequest().authenticated();
    }
}

第四步:?jiǎn)?dòng)服務(wù)進(jìn)行訪問測(cè)試(分別用正確和錯(cuò)誤的賬號(hào)進(jìn)行測(cè)試)。

放行靜態(tài)資源

在SecurityManager配置類中的configure(HttpSecurity http)方法中我們可以通過對(duì)anMatchers方法定義要放行靜態(tài)資源,例如:

.authorizeRequests() //設(shè)置請(qǐng)求的授權(quán)
        .antMatchers(   //配置下列路徑的授權(quán)
                "/index.html",
                "/js/*",
                "/css/*",
                "/img/**",
                "/bower_components/**",
                "/login.html"
        ).permitAll()   //設(shè)置上述所有路徑不需要登錄就能訪問(放行)

其中:

  • “*”用于匹配0個(gè)或多個(gè)字符

  • “**”用于匹配0個(gè)或多個(gè)目錄及字符

登出設(shè)計(jì)及實(shí)現(xiàn)

在SecurityManager配置類中的configure(HttpSecurity http)方法中,添加登出配置,例如

http.logout()       //開始設(shè)置登出信息
        .logoutUrl("/logout")   //登出路徑
        .logoutSuccessUrl("/login.html?logout");//設(shè)置登出后顯示的頁(yè)面

SpringSecurity授權(quán)邏輯實(shí)現(xiàn)

修改授權(quán)配置類

在權(quán)限配置類上添加啟用全局方法訪問控制注解,例如:

package com.cy.auth.config;
//這個(gè)配置類是配置Spring-Security的,
//prePostEnabled= true表示啟動(dòng)權(quán)限管理功能
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Configuration
public class SpringSecurityConfigurer extends WebSecurityConfigurerAdapter {
    ……
}

定義資源Controller

定義一個(gè)ResourceController類,作為資源訪問對(duì)象,例如

package com.cy.jt.auth.controller;

@RestController
public class ResourceController {
    @PreAuthorize("hasAuthority('sys:res:create')")
    @RequestMapping("/doCreate")
    public String doCreate(){
        return "add resource";
    }
    @PreAuthorize("hasAuthority('sys:res:update')")
    @RequestMapping("doUpdate")
    public String doUpdate(){
        return "update resource";
    }
    @PreAuthorize("hasAuthority('sys:res:delete')")
    @RequestMapping("/doDelete")
    public String doDelete(){
        return "delete resource";
    }
    @PreAuthorize("hasAuthority('sys:res:retrieve')")
    @RequestMapping("/doRetrieve")
    public String doRetrieve(){
        return "retrieve resource";
    }
}

其中,@PreAuthorize注解描述方法時(shí),用于告訴系統(tǒng)訪問此方法時(shí)需要進(jìn)行權(quán)限檢測(cè)。需要具備指定權(quán)限才可以訪問。例如:

  • @PreAuthorize(“hasAuthority('sys:res:delete”) 需要具備sys:res:delete權(quán)限

  • @PreAuthorize(“hasRole(‘a(chǎn)dmin')”) 需要具備admin角色 啟動(dòng)服務(wù)訪問測(cè)試

使用不同用戶進(jìn)行登陸,然后執(zhí)行資源訪問,假如沒有權(quán)限,則會(huì)看到響應(yīng)狀態(tài)嗎403,如圖所示:

Spring Security 安全框架的原理是什么

Spring認(rèn)證和授權(quán)異常處理

異常類型

對(duì)于SpringSecurity框架而言,在實(shí)現(xiàn)認(rèn)證和授權(quán)業(yè)務(wù)時(shí),可能出現(xiàn)如下兩大類型異常:
1)AuthenticationException (用戶還沒有認(rèn)證就去訪問某個(gè)需要認(rèn)證才可訪問的方法時(shí),可能出現(xiàn)的異常,這個(gè)異常通常對(duì)應(yīng)的狀態(tài)碼401)
2)AccessDeniedException (用戶認(rèn)證以后,在訪問一些沒有權(quán)限的資源時(shí),可能會(huì)出現(xiàn)的異常,這個(gè)異常通常對(duì)應(yīng)的狀態(tài)嗎為403)

異常處理規(guī)范

SpringSecurity框架給了默認(rèn)的異常處理方式,當(dāng)默認(rèn)的異常處理方式不滿足我們實(shí)際業(yè)務(wù)需求時(shí),此時(shí)我們就要自己定義異常處理邏輯,編寫邏輯時(shí)需要遵循如下規(guī)范:
1)AuthenticationEntryPoint:統(tǒng)一處理 AuthenticationException 異常
2)AccessDeniedHandler:統(tǒng)一處理 AccessDeniedException 異常.

自定義異常處理對(duì)象

處理沒有認(rèn)證的訪問異常

package com.cy.jt.config;
public class DefaultAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request,
                         HttpServletResponse response,
                         AuthenticationException e) throws IOException, ServletException {
        //設(shè)置響應(yīng)數(shù)據(jù)的編碼
        response.setCharacterEncoding("utf-8");
        //告訴瀏覽器要響應(yīng)的內(nèi)容類型,以及編碼
        response.setContentType("application/json;charset=utf-8");
        Map<String,Object> map=new HashMap<>();
        map.put("state",401);
        map.put("message","請(qǐng)先登錄");
        PrintWriter out=response.getWriter();
        out.println(new ObjectMapper().writeValueAsString(map));
        out.flush();
        out.close();
    }
}

處理沒有權(quán)限時(shí)拋出的異常

package com.cy.jt.config;

public class DefaultAccessDeniedExceptionHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request,
                       HttpServletResponse response,
                       AccessDeniedException e) throws IOException, ServletException {
        //設(shè)置響應(yīng)數(shù)據(jù)的編碼
        response.setCharacterEncoding("utf-8");
        //告訴瀏覽器要響應(yīng)的內(nèi)容類型,以及編碼
        response.setContentType("application/json;charset=utf-8");
        Map<String,Object> map=new HashMap<>();
        map.put("state",403);
        map.put("message","沒有此資源的訪問權(quán)限");
        PrintWriter out=response.getWriter();
        out.println(new ObjectMapper().writeValueAsString(map));
        out.flush();
        out.close();
    }
}

配置異常處理對(duì)象

在配置類SecurityConfig中添加自定義異常處理對(duì)象,代碼如下

 http.exceptionHandling()
            .authenticationEntryPoint(new DefaultAuthenticationEntryPoint())
            .accessDeniedHandler(new DefaultAccessDeniedExceptionHandler());

配置完成后,重啟服務(wù)進(jìn)行訪問測(cè)試分析.

系統(tǒng)會(huì)話狀態(tài)分析與實(shí)踐 何為會(huì)話狀態(tài)

客戶端與服務(wù)端通訊過程中產(chǎn)生的狀態(tài)信息(類似會(huì)議記錄),稱之為會(huì)話狀態(tài).

會(huì)話狀態(tài)如何存儲(chǔ)

客戶端瀏覽器與服務(wù)端通訊時(shí)使用的是http協(xié)議,這個(gè)協(xié)議本身是無(wú)狀態(tài)協(xié)議,也就是說通過此協(xié)議,無(wú)法存儲(chǔ)會(huì)話狀態(tài),此時(shí)在服務(wù)端與客戶端就采用了一種Cookie與Session方式記錄會(huì)話狀態(tài).

有狀態(tài)的會(huì)話技術(shù)分析

  •  Cookie 技術(shù)

Cookie是由服務(wù)端創(chuàng)建但在客戶端存儲(chǔ)會(huì)話狀態(tài)的一個(gè)對(duì)象,此對(duì)象分為兩種類型,一種為會(huì)話Cookie,一種為持久Cookie,瀏覽器在訪問具體的某個(gè)域名時(shí)會(huì)攜帶這個(gè)域的有效Cookie到服務(wù)端.

  1. 會(huì)話Cookie: 瀏覽器關(guān)閉Cookie生命周期結(jié)束(一般默認(rèn)都是會(huì)話Cookie)

  2. 持久Cookie: 持久Cookie是在Cookie對(duì)象創(chuàng)建時(shí)指定了生命周期,例如一周時(shí)間,即便瀏覽器關(guān)閉,持久Cookie依舊有效.

  • Session技術(shù)

Session技術(shù)由服務(wù)端創(chuàng)建,并在服務(wù)端存儲(chǔ)會(huì)話狀態(tài)的一個(gè)對(duì)象,當(dāng)Session對(duì)象創(chuàng)建時(shí),還會(huì)創(chuàng)建一個(gè)會(huì)話Cookie對(duì)象,并且通過這個(gè)會(huì)話Cookie將SessionId寫到客戶端,客戶端下次訪問服務(wù)端會(huì)攜帶這個(gè)會(huì)話Cookie,并且通過JsessionId找到Session對(duì)象,進(jìn)而獲取Session對(duì)象中存儲(chǔ)的數(shù)據(jù).Cookie默認(rèn)的生命周期為30分鐘.

在SpringSecurity中獲取用戶的認(rèn)證信息,就可以通過如下方式進(jìn)行實(shí)現(xiàn):

 Authentication authentication =
                SecurityContextHolder.getContext().getAuthentication();

無(wú)狀態(tài)的會(huì)話技術(shù)分析

有狀態(tài)的會(huì)話實(shí)現(xiàn),在分布式架構(gòu)中可能會(huì)存在很多問題,例如瀏覽器默認(rèn)不支持?jǐn)y帶其它域的Cookie信息進(jìn)行資源訪問,同時(shí)服務(wù)端的Session默認(rèn)不能共享,當(dāng)然我們有一種方式可以將session持久化到到一些數(shù)據(jù)庫(kù),例如Redis,下次請(qǐng)求到其它服務(wù)器(例如tomcat)時(shí),可以直接從redis中獲取登錄信息,但是假如并發(fā)比較大,數(shù)據(jù)庫(kù)的訪問壓力就會(huì)劇增,壓力太大有可能會(huì)導(dǎo)致系統(tǒng)宕機(jī).所以現(xiàn)在還有一種方案就是將用戶的登錄狀態(tài)信息都存儲(chǔ)在客戶端,服務(wù)端不記錄任何狀態(tài),服務(wù)端只負(fù)責(zé)對(duì)客戶端傳遞過來的狀態(tài)信息進(jìn)行解析,基于此方式進(jìn)行用戶登錄狀態(tài)的判斷,這樣的會(huì)話過程稱之為無(wú)狀態(tài)會(huì)話.

以上就是Spring Security 安全框架的原理是什么,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見到或用到的。希望你能通過這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注億速云行業(yè)資訊頻道。

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

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

AI