欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring?Security登錄表單配置示例詳解

 更新時(shí)間:2022年06月20日 09:13:03   作者:目目沐沐  
這篇文章主要介紹了Spring?Security登錄表單配置,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

Spring Security登錄表單配置

1.引入pom依賴

? 創(chuàng)建一個(gè)Spring Boot工程,引入WebSpring Security依賴:

<?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>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
    </parent>

    <groupId>com.kapcb.ccc</groupId>
    <artifactId>springsecurity-helloworld</artifactId>
    <version>1.0-SNAPSHOT</version>

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

</project>

2.bootstrap.yml添加配置

? 工程創(chuàng)建完成之后,為了方便測(cè)試,需要在bootstrap.yml配置文件中添加如下配置,將登錄用戶名和密碼固定下來:

spring:
  security:
    user:
      name: kapcb
      password: 123456

3.創(chuàng)建login.html

? 在resources/static目錄下創(chuàng)建login.html頁(yè)面,這個(gè)就是自定義登錄頁(yè)面:

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>

<form action="/loginSystem" method="post">
    <label>username: <input type="text" name="username"/></label></br>
    <label>password: <input type="password" name="password"/></label></br>
    <input type="submit" name="submit" value="login">
</form>

</body>
</html>

? 這個(gè)login.html核心內(nèi)容就是一個(gè)登陸表單,登陸表單中有三個(gè)需要注意的地方:

  • form標(biāo)簽中的action熟悉,這里是/loginSystem,表示表單要提交請(qǐng)求到/loginSystem接口上。
  • 用戶名框的name屬性值為username。
  • 密碼框的name屬性為password

4.創(chuàng)建配置類

? 定義好login.html之后,接下來就定義兩個(gè)測(cè)試接口,作為受保護(hù)資源。當(dāng)用戶登陸成功后,就可以訪問受保護(hù)的資源。接口定義如下:

package com.kapcb.security.helloworld.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * <a>Title: HelloWorldController </a>
 * <a>Author: Kapcb <a>
 * <a>Description: HelloWorldController <a>
 *
 * @author Kapcb
 * @version 1.0
 * @date 2022/6/12 22:05
 * @since 1.0
 */
@RestController
public class HelloWorldController {

    @GetMapping("index")
    public String index() {
        return "login success!";
    }

    @GetMapping("hello")
    public String hello() {
        return "Hello, World!";
    }

}

? 最后再提供Spring Security的配置類,需要注意的是在Spring Security 5.7版本中已經(jīng)廢棄WebSecurityConfigurerAdapter

package com.kapcb.security.helloworld.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

/**
 * <a>Title: SecurityConfiguration </a>
 * <a>Author: Kapcb <a>
 * <a>Description: SecurityConfiguration <a>
 *
 * @author Kapcb
 * @version 1.0
 * @date 2022/6/13 22:23
 * @since 1.0
 */
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/loginSystem")
                .defaultSuccessUrl("/index")
                .failureUrl("/login.html")
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
                .and()
                .csrf().disable().build();
    }

}

? 這里直接使用聲明SecurityFilterChain類型的Bean的方式即可配置Spring Security登陸表單。

  • authorizeHttpRequests()方法表示開啟權(quán)限配置。
  • anyRequest().authenticated():表示所有請(qǐng)求都需要經(jīng)過認(rèn)證。
  • and():方法會(huì)返回httpSecurityBuilder對(duì)象的一個(gè)子類,實(shí)際上就是HttpSecurity。所以and()方法相當(dāng)于返回一個(gè)HttpSecurity實(shí)例,重新開啟新一輪配置。
  • formLogin():表示開啟表單登陸配置。
  • loginPage("/login.html"):用于配置默認(rèn)登錄頁(yè)的請(qǐng)求地址。
  • loginProcessingUrl("/loginSystem"):用于配置登錄請(qǐng)求接口地址。
  • defaultSuccessUrl("/index"):表示登錄請(qǐng)求處理成功后的跳轉(zhuǎn)地址。
  • failureUrl("/login.html"):表示登陸失敗跳轉(zhuǎn)的地址。
  • usernameParameter("username"):表示登陸用戶名的參數(shù)名稱。
  • passwordParameter("password"):表示登錄密碼的參數(shù)名稱。
  • permitAll():表示跟登錄相關(guān)的頁(yè)面和接口不做攔截,直接允許訪問。
  • csrf().disable():表示禁用CSRF防御功能。Spring Security自帶了CSRF防御機(jī)制。為了測(cè)試方便,這里先關(guān)閉了。

? 需要注意的是,上面的loginPage()、loginProcessingUrl()defaultSuccessUrl()、usernameParameter()passwordParameter()需要和之前創(chuàng)建的login.html中登錄表單的配置一致。

? 完成上述配置之后,啟動(dòng)工程,訪問http://localhost:9096/index,會(huì)自動(dòng)跳轉(zhuǎn)到http://localhost:9096/login.html頁(yè)面。輸入之前設(shè)置好的用戶名密碼,登陸成功之后,就可以訪問到/index頁(yè)面了。

5.配置細(xì)節(jié)

? 在前面的配置中,使用defaultSuccessUrl()方法配置了用戶登陸成功之后的跳轉(zhuǎn)地址。使用failureUrl()方法配置了用戶登錄失敗之后的跳轉(zhuǎn)地址。關(guān)于用戶登錄成功與登陸失敗,除了這兩個(gè)方法可以進(jìn)行配置之外,還有另外兩個(gè)方法也可配置。

6.登陸成功

? 當(dāng)用戶登錄成功之后,除了使用defaultSuccessUrl()方法可以實(shí)現(xiàn)登錄成功后的跳轉(zhuǎn)之外,successForwardUrl()方法也可以配置實(shí)現(xiàn)登錄成功之后的跳轉(zhuǎn),配置代碼如下:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/loginSystem")
                .successForwardUrl("/index")
                .failureUrl("/login.html")
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
                .and()
                .csrf().disable().build();
    }
}

? defaultSuccessUrl()successForwardUrl()方法配置的區(qū)別如下:

  • defaultSuccessUrl()方法表示當(dāng)用戶登陸成功之后,會(huì)自動(dòng)重定向到登錄之前用戶訪問的地址上。如果用戶本身就是直接訪問的登陸頁(yè)面,則登錄成功之后就會(huì)重定向到defaultSuccessUrl()指定的頁(yè)面。
  • successForwardUrl()方法則不會(huì)考慮用戶登錄之前訪問的地址,只要用戶登陸成功,就會(huì)通過服務(wù)器端跳轉(zhuǎn)到successForwardUrl()所指定的頁(yè)面。
  • defaultSuccessUrl()方法有一個(gè)重載方法,如果重載方法的第二個(gè)參數(shù)傳入true,則defaultSuccessUrl()方法的效果與successForwardUrl()方法類似,即不考慮用戶之前的訪問地址,只要登陸成功,就重定向到defaultSuccessUrl()所指定的頁(yè)面。不同之處在于,defaultSuccessUrl()方法是通過重定向?qū)崿F(xiàn)的客戶端跳轉(zhuǎn),successForwardUrl()則是通過服務(wù)端跳轉(zhuǎn)實(shí)現(xiàn)的。

? 無論是defaultSuccessUrl()還是successForwardUrl()方法配置登錄成功跳轉(zhuǎn)頁(yè)面,最終所配置的都是AuthenticationSuccessHandler接口的實(shí)例。

? 在Spring Security中專門提供了AuthenticationSuccessHandler接口來處理用戶登陸成功事項(xiàng),AuthenticationSuccessHandler接口源碼如下:

public interface AuthenticationSuccessHandler {
    default void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
        this.onAuthenticationSuccess(request, response, authentication);
        chain.doFilter(request, response);
    }

    void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException;
}

? AuthenticationSuccessHandler接口中定義了兩個(gè)方法,其中一個(gè)是default方法,此方法是Spring Security 5.2版本中添加進(jìn)來的,在處理特定的認(rèn)證請(qǐng)求Authentication Filter中會(huì)用到。另外一個(gè)非default方法,則用來處理登陸成功的具體事項(xiàng),其中Authentication參數(shù)保存了登陸成功的用戶信息。

? AuthenticationSuccessHandler接口在Spring Security源碼中一共有三個(gè)實(shí)現(xiàn)類:

  • SimpleUrlAuthenticationSuccessHandler繼承自AbstractAuthenticationTargetUrlRequestHandler。通過AbstractAuthenticationTargetUrlRequestHandler中的handler()方法實(shí)現(xiàn)請(qǐng)求重定向。
  • SavedRequestAwareAuthenticationSuccessHandler在SimpleUrlAuthenticationSuccessHandler的基礎(chǔ)上增加了請(qǐng)求緩存的功能,可以記錄用戶登陸之前請(qǐng)求的地址,進(jìn)而在登陸成功之后重定向到一開始訪問的地址。
  • ForwardAuthenticationSuccessHandler的實(shí)現(xiàn)比較簡(jiǎn)單,就是一個(gè)服務(wù)端跳轉(zhuǎn)。

? 通過defaultSuccessUrl()方法來配置登陸成功之后重定向的請(qǐng)求地址時(shí),實(shí)際上對(duì)應(yīng)的實(shí)現(xiàn)類就是SavedRequestAwareAuthenticationSuccessHandler。SavedRequestAwareAuthenticationSuccessHandler源碼如下:

public class SavedRequestAwareAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
    protected final Log logger = LogFactory.getLog(this.getClass());
    private RequestCache requestCache = new HttpSessionRequestCache();

    public SavedRequestAwareAuthenticationSuccessHandler() {
    }

    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
        SavedRequest savedRequest = this.requestCache.getRequest(request, response);
        if (savedRequest == null) {
            super.onAuthenticationSuccess(request, response, authentication);
        } else {
            String targetUrlParameter = this.getTargetUrlParameter();
            if (!this.isAlwaysUseDefaultTargetUrl() && (targetUrlParameter == null || !StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
                this.clearAuthenticationAttributes(request);
                String targetUrl = savedRequest.getRedirectUrl();
                this.getRedirectStrategy().sendRedirect(request, response, targetUrl);
            } else {
                this.requestCache.removeRequest(request, response);
                super.onAuthenticationSuccess(request, response, authentication);
            }
        }
    }

    public void setRequestCache(RequestCache requestCache) {
        this.requestCache = requestCache;
    }
}

? SavedRequestAwareAuthenticationSuccessHandler中的核心方法就是onAuthenticationSuccess()

  • 從RequestCache中獲取緩存下來的請(qǐng)求,如果沒有獲取到緩存的請(qǐng)求地址,就代表用戶在登錄之前并沒有訪問其它頁(yè)面。此時(shí)直接調(diào)用父類的onAuthenticationSuccess()方法來處理,最終重定向到defaultSuccessUrl()方法指定的地址。
  • 接下里會(huì)獲取一個(gè)targetUrlParameter,這個(gè)地址是用戶顯示指定的、希望登錄成功之后重定向的地址。例如用戶發(fā)送的登錄請(qǐng)求是http://127.0.0.1:9096/loginSystem?target=/hello,這就表示當(dāng)用戶登陸成功之后,希望主動(dòng)重定向到/hello接口。targetUrlParameter()方法就是獲取重定向地址參數(shù)的key,就是上面舉例中的target。獲取到target之后就可以獲取到重定向的地址了。
  • 如果targetUrlParameter存在,或者開發(fā)者設(shè)置了isAlwaysUseDefaultTargetUrl()為true,此時(shí)緩存下來的請(qǐng)求就失去了意義。此時(shí)會(huì)直接移除掉緩存的請(qǐng)求地址,直接調(diào)用父類的onAuthenticationSuccess()方法完成重定向。targetUrlParameter存在,則直重定向到targetUrlParameter指定的地址。isAlwaysUseDefaultTargetUrl為true,則直接重定向到defaultSuccessUrl指定的地址。如果targetUrlParameter存在并且isAlwaysUseDefaultTargetUrl為true,則直接重定向到defaultSuccessUrl指定的地址。
  • 如果上述條件均不滿足,那么最終會(huì)從緩存請(qǐng)求SavedRequest對(duì)象中獲取重定向地址,然后進(jìn)行重定向操作。

? 這就是SavedRequestAwareAuthenticationSuccessHandler的實(shí)現(xiàn)邏輯,開發(fā)者也可配置自己的SavedRequestAwareAuthenticationSuccessHandler,如下:

package com.kapcb.security.helloworld.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;

/**
 * <a>Title: SecurityConfiguration </a>
 * <a>Author: Kapcb <a>
 * <a>Description: SecurityConfiguration <a>
 *
 * @author Kapcb
 * @version 1.0
 * @date 2022/6/13 22:23
 * @since 1.0
 */
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/loginSystem")
                .successHandler(successHandler())
                .failureUrl("/login.html")
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
                .and()
                .csrf().disable().build();
    }

    protected SavedRequestAwareAuthenticationSuccessHandler successHandler() {
        SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
        successHandler.setDefaultTargetUrl("/index");
        successHandler.setTargetUrlParameter("target");
        return successHandler;
    }

}

? 在上面的配置代碼中,制定了targetUrlParametertarget,這樣用戶就可以在登錄請(qǐng)求中,通過target來指定跳轉(zhuǎn)地址。修改一下上面的login.html中的form表單:

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>

<form action="/loginSystem?target=/hello" method="post">
    <label>username: <input type="text" name="username"/></label></br>
    <label>password: <input type="password" name="password"/></label></br>
    <input type="submit" name="submit" value="login">
</form>

</body>
</html>

? 修改form表單的action屬性為/loginSystem?target=/hello。當(dāng)用戶登陸成功之后,就始終會(huì)跳轉(zhuǎn)到/hello接口了。

? 在使用successForwardUrl()方法設(shè)置登陸成功后重定向的地址時(shí),實(shí)際上對(duì)應(yīng)的實(shí)現(xiàn)類是ForwardAuthenticationSuccessHandlerForwardAuthenticationSuccessHandler類的源碼非常簡(jiǎn)單,就是一個(gè)服務(wù)端轉(zhuǎn)發(fā),ForwardAuthenticationSuccessHandler源碼如下:

public class ForwardAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    private final String forwardUrl;

    public ForwardAuthenticationSuccessHandler(String forwardUrl) {
        Assert.isTrue(UrlUtils.isValidRedirectUrl(forwardUrl), () -> {
            return "'" + forwardUrl + "' is not a valid forward URL";
        });
        this.forwardUrl = forwardUrl;
    }
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        request.getRequestDispatcher(this.forwardUrl).forward(request, response);
    }
}

? 主要功能就是調(diào)用request.getRequestDispatcher(this.forwardUrl).forward(request, response)方法實(shí)現(xiàn)服務(wù)端請(qǐng)求轉(zhuǎn)發(fā)。

? 啟動(dòng)服務(wù)之后訪問登錄頁(yè)面,登錄成功后即可自動(dòng)重定向到/hello接口。

? AuthenticationSuccessHandler默認(rèn)的三個(gè)實(shí)現(xiàn)類,無論是哪一種,都是用來處理頁(yè)面跳轉(zhuǎn)的。隨著前后端分離架構(gòu)的盛行,頁(yè)面跳轉(zhuǎn)并不能滿足我們的業(yè)務(wù)需求,用戶登錄成功后,后端返回JSON數(shù)據(jù)給前端即可。告訴前端當(dāng)前用戶是登陸成功還是失敗即可,前端拿到后端響應(yīng)結(jié)果后自行處理即可。像這種需求,可以使用自定義AuthenticationSuccessHandler的實(shí)現(xiàn)類完成。

package com.kapcb.security.helloworld.handler;

import com.alibaba.fastjson.JSON;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * <a>Title: CustomizeAuthenticationSuccessHandler </a>
 * <a>Author: Kapcb <a>
 * <a>Description: CustomizeAuthenticationSuccessHandler <a>
 *
 * @author Kapcb
 * @version 1.0
 * @date 2022/6/16 23:47
 * @since 1.0
 */
public class CustomizeAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        response.setContentType("application/json;charset=utf-8");
        Map<String, Object> resultMap = new HashMap<>(4);
        resultMap.put("code", 200);
        resultMap.put("msg", null);
        resultMap.put("data", "1111111");
        String jsonResult = JSON.toJSONString(resultMap);
        response.getWriter().write(jsonResult);
        response.getWriter().close();
    }

}

? 修改配置類:

package com.kapcb.security.helloworld.configuration;

import com.kapcb.security.helloworld.handler.CustomizeAuthenticationSuccessHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;

/**
 * <a>Title: SecurityConfiguration </a>
 * <a>Author: Kapcb <a>
 * <a>Description: SecurityConfiguration <a>
 *
 * @author Kapcb
 * @version 1.0
 * @date 2022/6/13 22:23
 * @since 1.0
 */
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/loginSystem")
                .successHandler(customizeSuccessHandler())
                .failureUrl("/login.html")
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
                .and()
                .csrf().disable().build();
    }

    protected CustomizeAuthenticationSuccessHandler customizeSuccessHandler() {
        return new CustomizeAuthenticationSuccessHandler();
    }
}

? 在使用自定義AuthenticationSuccessHandler的實(shí)現(xiàn)類來完成登錄成功之后的處理邏輯之后,還需要同步修改login.html。

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>

<form action="/loginSystem" method="post">
    <label>username: <input type="text" name="username"/></label></br>
    <label>password: <input type="password" name="password"/></label></br>
    <input type="submit" name="submit" value="login">
</form>

</body>
</html>

? 將form表單中的action屬性修改為之前的/loginSystem。因?yàn)榇藭r(shí)使用的是自定義的AuthenticationSuccessHandler。邏輯與SavedRequestAwareAuthenticationSuccessHandler是不一樣的。

? 所有的改動(dòng)完成之后,重啟工程。當(dāng)用戶登錄成功之后,就不會(huì)在進(jìn)行頁(yè)面跳轉(zhuǎn)了,而是返回了一段JSON字符串到頁(yè)面上。

7.登陸失敗

? Spring Security登陸失敗的處理邏輯。為了方便在前端頁(yè)面展示登錄失敗的異常信息,首先在項(xiàng)目的pom.xml文件中引入thymeleaf模板引擎的依賴:

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

? 在resources目錄下創(chuàng)建templates文件夾,新建loginTemplate.html文件:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>loginTemplate</title>
</head>
<body>
<form action="/loginSystem" method="post">
    <div th:text="${SPRING_SECURITY_LAST_EXCEPTION}"></div>
    <label>username: <input type="text" name="username"/></label></br>
    <label>password: <input type="password" name="password"/></label></br>
    <input type="submit" name="submit" value="login">
</form>
</body>
</html>

? loginTemplate.html文件和前面的login.html文件基本類似。login.htmlstatic目錄下,屬于靜態(tài)頁(yè)面,loginTemplate.htmltemplate模板頁(yè)面。loginTemplate.htmlform標(biāo)簽內(nèi)新增了<div th:text="${SPRING_SECURITY_LAST_EXCEPTION}"></div>標(biāo)簽,用于展示Spring Security在處理登陸失敗時(shí)的異常信息。在Spring Security中,用戶登陸失敗時(shí),異常信息會(huì)放在request中返回給前端,開發(fā)者可將其直接提取出來展示。

? loginTemplate.html屬于動(dòng)態(tài)頁(yè)面,所以就不能像訪問static/login.html那樣直接訪問,需要后端為其提供訪問控制器:

package com.kapcb.security.helloworld.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * <a>Title: RouterController </a>
 * <a>Author: Kapcb <a>
 * <a>Description: RouterController <a>
 *
 * @author Kapcb
 * @version 1.0
 * @date 2022/6/17 23:32
 * @since 1.0
 */
@Controller
@RequestMapping("/router")
public class RouterController {

    @RequestMapping("loginTemplate")
    public String loginTemplate() {
        return "loginTemplate";
    }
}

? 最后在Spring Security配置類中配置登陸頁(yè)面:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .antMatchers("/loginFail")
                .permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/router/loginTemplate")
                .loginProcessingUrl("/loginSystem")
                .successHandler(customizeSuccessHandler())
                .failureUrl("/router/loginTemplate")
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
                .and()
                .csrf().disable().build();
    }

    protected CustomizeAuthenticationSuccessHandler customizeSuccessHandler() {
        return new CustomizeAuthenticationSuccessHandler();
    }
}

? failureUrl()表示登陸失敗后重定向到/router/loginTemplate接口請(qǐng)求地址,也就是loginTemplate.html頁(yè)面。重定向是一種客戶端跳轉(zhuǎn),重定向不方便攜帶請(qǐng)求失敗的異常信息,只能放在URL中。

? 如果希望在前端展示請(qǐng)求失敗的異常信息,可以使用下面這種方式:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .antMatchers("/loginFail")
                .permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/router/loginTemplate")
                .loginProcessingUrl("/loginSystem")
                .successHandler(customizeSuccessHandler())
                .failureForwardUrl("/router/loginTemplate")
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
                .and()
                .csrf().disable().build();
    }

    protected CustomizeAuthenticationSuccessHandler customizeSuccessHandler() {
        return new CustomizeAuthenticationSuccessHandler();
    }
}

? failureForwardUrl()方法從名字上就可以看出,這種跳轉(zhuǎn)是一種服務(wù)器端跳轉(zhuǎn)。服務(wù)器端跳轉(zhuǎn)的好處是可以攜帶登錄異常信息。如果用戶登陸失敗,自動(dòng)跳轉(zhuǎn)回登錄頁(yè)面后,就可以將錯(cuò)誤信息展示出來。

登錄失敗服務(wù)端跳轉(zhuǎn)攜帶異常信息

? 無論是failureUrl()還是failureForwardUrl()方法,最終所配置的都是AuthenticationFailureHandler接口的實(shí)現(xiàn)類。Spring Security中提供了AuthenticationFailureHandler接口,為登陸失敗下的處理方式提供頂級(jí)拓展。AuthenticationFailureHandler源碼如下:

public interface AuthenticationFailureHandler {
    void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException;
}

? AuthenticationFailureHandler接口中只定義了一個(gè)onAuthenticationFailure()方法,用于處理登陸失敗的請(qǐng)求。AuthenticationException表示登陸失敗的異常信息。Spring Security中為AuthenticationFailureHandler一共提供了五個(gè)實(shí)現(xiàn)類:

  • SimpleUrlAuthenticationFailureHandler:默認(rèn)的處理邏輯就是通過重定向跳轉(zhuǎn)到登陸頁(yè)面,當(dāng)然也可以通過配置forwardToDestination屬性將重定向改為服務(wù)器端跳轉(zhuǎn),failureUrl()方法底層實(shí)現(xiàn)邏輯就是SimpleUrlAuthenticationFailureHandler。
  • ExceptionMappingAuthenticationFailureHandler:可以實(shí)現(xiàn)根據(jù)不同異常類型,映射到不同路徑。
  • ForwardAuthenticationFailureHandler:表示通過服務(wù)器端跳轉(zhuǎn)來重新回到登陸頁(yè)面,failureForwardUrl()方法底層實(shí)現(xiàn)邏輯就是ForwardAuthenticationFailureHandler。
  • AuthenticationEntryPointFailureHandler:是Spring Security 5.2新引入的處理類,可以通過AuthenticationEntryPoint來處理登陸異常。
  • DelegatingAuthenticationFailureHandler:可以實(shí)現(xiàn)為不同的異常類型配置不同的登錄失敗處理回調(diào)。

? 舉個(gè)簡(jiǎn)單的例子。假設(shè)不使用failureForwardUrl()方法進(jìn)行登陸失敗的處理邏輯配置,同時(shí)又想在登陸失敗后通過服務(wù)器端跳轉(zhuǎn)回到登陸頁(yè)面,那么可以自定義SimpleUrlAuthenticationFailureHandler配置,并將forwardToDestination屬性設(shè)置為true,代碼如下:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .antMatchers("/loginFail")
                .permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/router/loginTemplate")
                .loginProcessingUrl("/loginSystem")
                .successHandler(customizeSuccessHandler())
                .failureHandler(failureHandler())
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
                .and()
                .csrf().disable().build();
    }

    protected CustomizeAuthenticationSuccessHandler customizeSuccessHandler() {
        return new CustomizeAuthenticationSuccessHandler();
    }

    protected SimpleUrlAuthenticationFailureHandler failureHandler() {
        SimpleUrlAuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
        failureHandler.setDefaultFailureUrl("/loginFail");
        failureHandler.setUseForward(true);
        return failureHandler;
    }

}

? 這樣配置之后,用戶再次登陸失敗,就會(huì)通過服務(wù)端跳轉(zhuǎn)重新回到登陸頁(yè)面,同時(shí)頁(yè)面上也會(huì)展示相應(yīng)的錯(cuò)誤信息,效果和failureForwardUrl()一樣。

? SimpleUrlAuthenticationFailureHandler的源碼也很簡(jiǎn)單:

public class SimpleUrlAuthenticationFailureHandler implements AuthenticationFailureHandler {
    protected final Log logger = LogFactory.getLog(this.getClass());
    private String defaultFailureUrl;
    private boolean forwardToDestination = false;
    private boolean allowSessionCreation = true;
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    public SimpleUrlAuthenticationFailureHandler() {
    }

    public SimpleUrlAuthenticationFailureHandler(String defaultFailureUrl) {
        this.setDefaultFailureUrl(defaultFailureUrl);
    }

    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        if (this.defaultFailureUrl == null) {
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Sending 401 Unauthorized error since no failure URL is set");
            } else {
                this.logger.debug("Sending 401 Unauthorized error");
            }

            response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
        } else {
            this.saveException(request, exception);
            if (this.forwardToDestination) {
                this.logger.debug("Forwarding to " + this.defaultFailureUrl);
                request.getRequestDispatcher(this.defaultFailureUrl).forward(request, response);
            } else {
                this.redirectStrategy.sendRedirect(request, response, this.defaultFailureUrl);
            }

        }
    }

    protected final void saveException(HttpServletRequest request, AuthenticationException exception) {
        if (this.forwardToDestination) {
            request.setAttribute("SPRING_SECURITY_LAST_EXCEPTION", exception);
        } else {
            HttpSession session = request.getSession(false);
            if (session != null || this.allowSessionCreation) {
                request.getSession().setAttribute("SPRING_SECURITY_LAST_EXCEPTION", exception);
            }

        }
    }

    public void setDefaultFailureUrl(String defaultFailureUrl) {
        Assert.isTrue(UrlUtils.isValidRedirectUrl(defaultFailureUrl), () -> {
            return "'" + defaultFailureUrl + "' is not a valid redirect URL";
        });
        this.defaultFailureUrl = defaultFailureUrl;
    }

    protected boolean isUseForward() {
        return this.forwardToDestination;
    }

    public void setUseForward(boolean forwardToDestination) {
        this.forwardToDestination = forwardToDestination;
    }

    public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
        this.redirectStrategy = redirectStrategy;
    }

    protected RedirectStrategy getRedirectStrategy() {
        return this.redirectStrategy;
    }

    protected boolean isAllowSessionCreation() {
        return this.allowSessionCreation;
    }

    public void setAllowSessionCreation(boolean allowSessionCreation) {
        this.allowSessionCreation = allowSessionCreation;
    }
}

? SimpleUrlAuthenticationFailureHandler提供了無參和有參構(gòu)造器。使用有參構(gòu)造器構(gòu)造SimpleUrlAuthenticationFailureHandler對(duì)象時(shí),可傳入defaultFailureUrl。defaultFailureUrl也就是登陸失敗時(shí)要跳轉(zhuǎn)的地址。

? onAuthenticationFailure()方法中,首先會(huì)判斷defaultFailureUrl是否為null,如果為null則直接通過response返回401狀態(tài)碼Unauthorized。

? 如果defaultFailureUrl不為空,則調(diào)用saveException()方。在saveException()方法中,如果forwardToDestination屬性為true,表示此時(shí)需要通過服務(wù)器端跳轉(zhuǎn)回登錄首頁(yè),此時(shí)就將異常信息放到request中。在回到onAuthenticationFailure()方法中,如果forwardToDestination屬性為true,就通過服務(wù)器端跳轉(zhuǎn)回到登錄頁(yè)面,否則通過重定向回到登陸頁(yè)面。

? 如果是前后端分離開發(fā),登陸失敗時(shí)就不需要進(jìn)行頁(yè)面跳轉(zhuǎn)了,只需要返回登錄失敗的JSON響應(yīng)給前端即可。這種場(chǎng)景下通過AuthenticationFailureHandler的實(shí)現(xiàn)類來完成,實(shí)現(xiàn)代碼如下:

public class CustomizeAuthenticationFailureHandler implements AuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        Map<String, Object> resultMap = new HashMap<>(4);
        resultMap.put("code", 500);
        resultMap.put("msg", exception.getMessage());
        resultMap.put("data", null);
        String jsonResult = JSON.toJSONString(resultMap);
        response.getWriter().write(jsonResult);
        response.getWriter().close();
    }

}

? 在Spring Security配置類中配置自定義登陸失敗處理器:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .antMatchers("/loginFail")
                .permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/router/loginTemplate")
                .loginProcessingUrl("/loginSystem")
                .successHandler(customizeSuccessHandler())
                .failureHandler(customizeFailureHandler())
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
                .and()
                .csrf().disable().build();
    }

    protected CustomizeAuthenticationSuccessHandler customizeSuccessHandler() {
        return new CustomizeAuthenticationSuccessHandler();
    }

    protected CustomizeAuthenticationFailureHandler customizeFailureHandler() {
        return new CustomizeAuthenticationFailureHandler();
    }

}

? 配置完成后,當(dāng)用戶再次登陸失敗,就不會(huì)進(jìn)行頁(yè)面跳轉(zhuǎn)了,而是直接返回JSON字符串。

8.注銷登錄

? Spring Security中提供了默認(rèn)的注銷頁(yè)面,開發(fā)者也可以根據(jù)自己的需求對(duì)注銷登錄進(jìn)行定制。

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .antMatchers("/loginFail")
                .permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/router/loginTemplate")
                .loginProcessingUrl("/loginSystem")
                .successHandler(customizeSuccessHandler())
                .failureHandler(customizeFailureHandler())
                .usernameParameter("username")
                .passwordParameter("password")
                .and()
                .logout()
                .logoutUrl("/logout")
                .invalidateHttpSession(true)
                .clearAuthentication(true)
                .permitAll()
                .and()
                .csrf().disable().build();
    }

    protected CustomizeAuthenticationSuccessHandler customizeSuccessHandler() {
        return new CustomizeAuthenticationSuccessHandler();
    }

    protected CustomizeAuthenticationFailureHandler customizeFailureHandler() {
        return new CustomizeAuthenticationFailureHandler();
    }

}
  • logout():表示開啟注銷登錄配置。
  • logoutUrl():表示指定了注銷登錄請(qǐng)求地址,默認(rèn)是GET請(qǐng)求,路徑為.logout。
  • invalidateHttpSession():表示是否使session失效,默認(rèn)為true。
  • clearAuthentication():表示是否清除認(rèn)真信息,默認(rèn)為true。
  • logoutSuccessUrl():表示注銷登錄后的跳轉(zhuǎn)地址。

? 配置完成后,再次啟動(dòng)工程,登陸成功后,在瀏覽器中輸入http://127.0.0.1:9096/logout就可以發(fā)起注銷登錄請(qǐng)求了。注銷成功后,會(huì)自動(dòng)跳轉(zhuǎn)到loginTemplate.html頁(yè)面。

? 開發(fā)者也可以配置多個(gè)注銷登錄的請(qǐng)求,同時(shí)還可以指定請(qǐng)求的方法:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .antMatchers("/loginFail")
                .permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/router/loginTemplate")
                .loginProcessingUrl("/loginSystem")
                .successHandler(customizeSuccessHandler())
                .failureHandler(customizeFailureHandler())
                .usernameParameter("username")
                .passwordParameter("password")
                .and()
                .logout()
                .logoutRequestMatcher(new OrRequestMatcher(
                        new AntPathRequestMatcher("/logout1", HttpMethod.GET.name()),
                        new AntPathRequestMatcher("/logout2", HttpMethod.POST.name())
                ))
                .invalidateHttpSession(true)
                .clearAuthentication(true)
                .logoutSuccessUrl("/router/loginTemplate")
                .permitAll()
                .and()
                .csrf().disable().build();
    }

    protected CustomizeAuthenticationSuccessHandler customizeSuccessHandler() {
        return new CustomizeAuthenticationSuccessHandler();
    }

    protected CustomizeAuthenticationFailureHandler customizeFailureHandler() {
        return new CustomizeAuthenticationFailureHandler();
    }

}

? 在logoutRequestMatcher()的配置表示注銷請(qǐng)求路徑,分別有兩個(gè):

  • 第一個(gè)是/logout1,請(qǐng)求方法是GET。
  • 第二個(gè)是/logout2,請(qǐng)求方法是POST

? 使用其中的任意一個(gè)請(qǐng)求都可以完成登陸注銷。

? 如果項(xiàng)目采用的是前后端分離架構(gòu),注銷成功后就需要頁(yè)面跳轉(zhuǎn)了。只需要將注銷成功的信息返回給前端即可,此時(shí)可以自定義返回內(nèi)容:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .antMatchers("/loginFail")
                .permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/router/loginTemplate")
                .loginProcessingUrl("/loginSystem")
                .successHandler(customizeSuccessHandler())
                .failureHandler(customizeFailureHandler())
                .usernameParameter("username")
                .passwordParameter("password")
                .and()
                .logout()
                .logoutRequestMatcher(new OrRequestMatcher(
                        new AntPathRequestMatcher("/logout1", HttpMethod.GET.name()),
                        new AntPathRequestMatcher("/logout2", HttpMethod.POST.name())
                ))
                .invalidateHttpSession(true)
                .clearAuthentication(true)
                .logoutSuccessHandler((request, response, auth) -> {
                    response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                    Map<String, Object> resultMap = new HashMap<>(4);
                    resultMap.put("code", 200);
                    resultMap.put("msg", "logout success!");
                    resultMap.put("data", null);
                    String jsonResult = JSON.toJSONString(resultMap);
                    response.getWriter().write(jsonResult);
                    response.getWriter().close();
                })
                .permitAll()
                .and()
                .csrf().disable().build();
    }

    protected CustomizeAuthenticationSuccessHandler customizeSuccessHandler() {
        return new CustomizeAuthenticationSuccessHandler();
    }

    protected CustomizeAuthenticationFailureHandler customizeFailureHandler() {
        return new CustomizeAuthenticationFailureHandler();
    }

}

? 配置logoutSuccessHandler()logoutSuccessUrl()類似于之前的successHandlerdefaultSuccessUrl之間的關(guān)系,只是類不同。

? 配置完成之后,重啟項(xiàng)目,登陸成功后再注銷登錄。無論是/logout1還是/logout2進(jìn)行注銷,只要注銷成功,就會(huì)返回JSON。

? 如果開發(fā)者希望為不同的注銷地址返回不同的結(jié)果,如下配置即可:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .antMatchers("/loginFail")
                .permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/router/loginTemplate")
                .loginProcessingUrl("/loginSystem")
                .successHandler(customizeSuccessHandler())
                .failureHandler(customizeFailureHandler())
                .usernameParameter("username")
                .passwordParameter("password")
                .and()
                .logout()
                .logoutRequestMatcher(new OrRequestMatcher(
                        new AntPathRequestMatcher("/logout1", HttpMethod.GET.name()),
                        new AntPathRequestMatcher("/logout2", HttpMethod.POST.name())
                ))
                .invalidateHttpSession(true)
                .clearAuthentication(true)
                .defaultLogoutSuccessHandlerFor((request, response, auth) -> {
                    response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                    Map<String, Object> resultMap = new HashMap<>(4);
                    resultMap.put("code", 200);
                    resultMap.put("msg", "logout1 success!");
                    resultMap.put("data", null);
                    String jsonResult = JSON.toJSONString(resultMap);
                    response.getWriter().write(jsonResult);
                    response.getWriter().close();
                }, new AntPathRequestMatcher("/logout1", HttpMethod.GET.name()))
                .defaultLogoutSuccessHandlerFor((request, response, auth) -> {
                    response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                    Map<String, Object> resultMap = new HashMap<>(4);
                    resultMap.put("code", 200);
                    resultMap.put("msg", "logout2 success!");
                    resultMap.put("data", null);
                    String jsonResult = JSON.toJSONString(resultMap);
                    response.getWriter().write(jsonResult);
                    response.getWriter().close();
                }, new AntPathRequestMatcher("/logout2", HttpMethod.POST.name()))
                .permitAll()
                .and()
                .csrf().disable().build();
    }

    protected CustomizeAuthenticationSuccessHandler customizeSuccessHandler() {
        return new CustomizeAuthenticationSuccessHandler();
    }

    protected CustomizeAuthenticationFailureHandler customizeFailureHandler() {
        return new CustomizeAuthenticationFailureHandler();
    }

}

? 通過defaultLogoutSuccessHandlerFor()方法可以注冊(cè)多個(gè)不同的注銷成功回調(diào)函數(shù),該方法第一個(gè)參數(shù)是注銷成功回調(diào),第二個(gè)參數(shù)則是具體的注銷登錄請(qǐng)求。當(dāng)用戶注銷成功之后,請(qǐng)求哪個(gè)注銷地址,就會(huì)返回對(duì)應(yīng)的響應(yīng)信息。

到此這篇關(guān)于Spring Security登錄表單配置的文章就介紹到這了,更多相關(guān)Spring Security登錄表單內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringMVC+EasyUI實(shí)現(xiàn)頁(yè)面左側(cè)導(dǎo)航菜單功能

    SpringMVC+EasyUI實(shí)現(xiàn)頁(yè)面左側(cè)導(dǎo)航菜單功能

    這篇文章主要介紹了SpringMVC+EasyUI實(shí)現(xiàn)頁(yè)面左側(cè)導(dǎo)航菜單功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-09-09
  • Mybatis傳遞多個(gè)參數(shù)的解決辦法(三種)

    Mybatis傳遞多個(gè)參數(shù)的解決辦法(三種)

    這篇文章主要介紹了Mybatis傳遞多個(gè)參數(shù)的解決辦法(三種),個(gè)人覺得第三種解決辦法比較好用,有需要的朋友一起學(xué)習(xí)吧
    2016-05-05
  • ShardingSphere JDBC強(qiáng)制路由使用的項(xiàng)目實(shí)踐

    ShardingSphere JDBC強(qiáng)制路由使用的項(xiàng)目實(shí)踐

    在某些特定場(chǎng)景下,可能需要繞過分片規(guī)則直接定位到特定的數(shù)據(jù)庫(kù)或表,這種情況下就可以使用HintRouting,本文就來介紹一下ShardingSphere JDBC強(qiáng)制路由使用的項(xiàng)目實(shí)踐,感興趣的可以了解一下
    2024-06-06
  • Java中的Semaphore信號(hào)量簡(jiǎn)析

    Java中的Semaphore信號(hào)量簡(jiǎn)析

    這篇文章主要介紹了Java中的Semaphore信號(hào)量簡(jiǎn)析,Semaphore:信號(hào)量,用來限制能同時(shí)訪問共享資源的線程上限,使用Semaphore實(shí)現(xiàn)簡(jiǎn)單連接池,對(duì)比享元模式下的實(shí)現(xiàn)(用wait和notify),性能和可讀性要更好,需要的朋友可以參考下
    2023-12-12
  • Java switch多值匹配操作詳解

    Java switch多值匹配操作詳解

    這篇文章主要介紹了Java switch多值匹配操作詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • Java基于Base64實(shí)現(xiàn)編碼解碼圖片文件

    Java基于Base64實(shí)現(xiàn)編碼解碼圖片文件

    這篇文章主要介紹了Java基于Base64實(shí)現(xiàn)編碼解碼圖片文件,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • 詳解Spring-boot中讀取config配置文件的兩種方式

    詳解Spring-boot中讀取config配置文件的兩種方式

    這篇文章主要介紹了詳解Spring-boot中讀取config配置文件的兩種方式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • 淺談Java中的高精度整數(shù)和高精度小數(shù)

    淺談Java中的高精度整數(shù)和高精度小數(shù)

    本篇文章主要介紹了淺談Java中的高精度整數(shù)和高精度小數(shù),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-08-08
  • 寧可用Lombok也不把成員設(shè)置為public原理解析

    寧可用Lombok也不把成員設(shè)置為public原理解析

    這篇文章主要為大家介紹了寧可用Lombok也不把成員設(shè)置為public原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • IDEA編譯亂碼Build Output提示信息亂碼

    IDEA編譯亂碼Build Output提示信息亂碼

    IDEA編譯的時(shí)候亂碼,Build Output提示信息亂碼,本文就詳細(xì)的介紹一下解決方法,有需要的同學(xué)可以了解一下
    2021-06-06

最新評(píng)論