最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringSecurity入門實戰(zhàn)應用場景

 更新時間:2026年04月09日 09:19:38   作者:漫霂  
本文總結(jié)了基于SpringSecurity的數(shù)據(jù)庫登錄認證實戰(zhàn),包括創(chuàng)建數(shù)據(jù)庫表、實體類、DBUserDetailsManager、配置、密碼加密、添加用戶、自定義SecurityUser、前后端響應處理、配置handler、跨域解決、身份認證概念解析及會話并發(fā)處理等內(nèi)容,感興趣的朋友一起看看吧

本文不著重講述SpringSecurity相關概念及其原理,而是致力于總結(jié)一些實戰(zhàn)應用場景

基于數(shù)據(jù)庫的登錄認證

1.創(chuàng)建數(shù)據(jù)庫表

-- 創(chuàng)建數(shù)據(jù)庫
CREATE DATABASE `security-demo`;
USE `security-demo`;
-- 創(chuàng)建用戶表
CREATE TABLE `user`(
	`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
	`username` VARCHAR(50) DEFAULT NULL ,
	`password` VARCHAR(500) DEFAULT NULL,
	`enabled` BOOLEAN NOT NULL
);
-- 唯一索引
CREATE UNIQUE INDEX `user_username_uindex` ON `user`(`username`); 
-- 插入用戶數(shù)據(jù)(密碼是 "abc" )
INSERT INTO `user` (`username`, `password`, `enabled`) VALUES
('admin', '{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW', TRUE),
('Helen', '{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW', TRUE),
('Tom', '{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW', TRUE);

2.創(chuàng)建實體類

@Data
public class User {
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    private String username;
    private String password;
    private Boolean enabled;
}

3.定義DBUserDetailsManager

DBUserDetailsManager要實現(xiàn)UserDetailsManager和UserDetailsPasswordService并重寫其抽象方法。

@Component  
public class DBUserDetailsManager implements UserDetailsManager, UserDetailsPasswordService {  
    @Autowired  
    private UserService userService;  
    @Override  
    public UserDetails updatePassword(UserDetails user, String newPassword) {  
        return null;  
    }  
    /**  
	 * 根據(jù)用戶名加載用戶詳情  
	 * @param username 用戶名  
	 * @return 用戶詳情  
	 * @throws UsernameNotFoundException 如果用戶不存在  
	 */  
	@Override  
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {  
	    User user = userService.lambdaQuery().eq(User::getUsername, username).one();  
	    if (user == null) {  
	        throw new UsernameNotFoundException("用戶不存在");  
	    }  
    // 返回自定義的安全用戶對象,將數(shù)據(jù)庫實體包裝在內(nèi)  
    return new SecurityUser(user);  
	}
    @Override  
    public void createUser(UserDetails user) {  
    }  
    @Override  
    public void updateUser(UserDetails user) {  
    }  
    @Override  
    public void deleteUser(String username) {  
    }  
    @Override  
    public void changePassword(String oldPassword, String newPassword) {  
    }  
    @Override  
    public boolean userExists(String username) {  
        return false;  
    }  
}

4.編輯配置

配置要根據(jù)項目實際情況進行選擇,如果不主動實現(xiàn)filterChain,框架也會給出一個默認實現(xiàn)

@Configuration  
@EnableMethodSecurity // 開啟方法級權(quán)限控制  
public class WebSecurityConfig {  
    @Bean  
    public PasswordEncoder passwordEncoder() {  
        return new BCryptPasswordEncoder();  
    }  
    @Bean  
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {  
        http  
            // 1. 配置請求授權(quán)  
            .authorizeHttpRequests(authorize -> authorize  
                    .requestMatchers("/user/add", "/login", "/error").permitAll() // 明確放行注冊、登錄和錯誤頁  
                    .requestMatchers("/static/**", "/css/**", "/js/**").permitAll() // 放行靜態(tài)資源  
                    .anyRequest().authenticated() // 剩下的所有請求都需要登錄  
            )  
            // 2. 自定義表單登錄邏輯  
            .formLogin(form -> form  
                    .defaultSuccessUrl("/", true) // 登錄成功后強制跳轉(zhuǎn)到首頁  
                    .permitAll()  
            )  
            // 3. 配置注銷功能  
            .logout(logout -> logout  
                    .logoutUrl("/logout") // 注銷接口地址  
                    .logoutSuccessUrl("/login?logout") // 注銷成功后跳轉(zhuǎn)回登錄頁并帶上提示  
                    .invalidateHttpSession(true) // 銷毀 Session                    .deleteCookies("JSESSIONID") // 刪除 Cookie                    .permitAll()  
            )  
            // 4. 防護配置  
            .csrf(csrf -> csrf.disable()); // Demo 階段保持禁用以便 API 測試  
        return http.build();  
    }  
}

密碼加密

1.配置passwordEncoder

config中添加PasswordEncoder,指明BCrypt加密算法

@Bean  
public PasswordEncoder passwordEncoder() {  
    return new BCryptPasswordEncoder();  
}

2.加密密碼

passwordEncoder.encode(user.getPassword())

添加用戶

1.自定義SecurityUser 類

自定義一個 SecurityUser 類作為包裝類既能保持實體類 User 的純粹性(不依賴 Spring Security 框架),也能在安全上下文中攜帶完整的業(yè)務數(shù)據(jù)。

public class SecurityUser implements UserDetails {  
    @Getter  
    private final User user; // 持有原始的數(shù)據(jù)庫實體對象  
    public SecurityUser(User user) {  
        this.user = user;  
    }  
    @Override  
    public String getUsername() {  
        return user.getUsername();  
    }  
    @Override  
    public String getPassword() {  
        return user.getPassword();  
    }  
    @Override  
    public Collection<? extends GrantedAuthority> getAuthorities() {  
        // 后續(xù)可以在此根據(jù) user 角色添加權(quán)限  
        return new ArrayList<>();  
    }  
    @Override  
    public boolean isAccountNonExpired() { return true; }  
    @Override  
    public boolean isAccountNonLocked() { return true; }  
    @Override  
    public boolean isCredentialsNonExpired() { return true; }  
    @Override  
    public boolean isEnabled() {  
        return user.getEnabled() != null && user.getEnabled();  
    }  
}

2.重寫DBUserDetailsManager中方法

@Override  
public void createUser(UserDetails userDetails) {  
    if (userDetails instanceof SecurityUser) {  
        // 如果是自定義類型,直接獲取內(nèi)部持有實體保存  
        userService.save(((SecurityUser) userDetails).getUser());  
    } else {  
        // 兜底邏輯:處理標準的 UserDetails 對象  
        User user = new User();  
        user.setUsername(userDetails.getUsername());  
        user.setPassword(userDetails.getPassword());  
        user.setEnabled(userDetails.isEnabled());  
        userService.save(user);  
    }  
}  
@Override  
public void updateUser(UserDetails user) {  
    // 根據(jù)業(yè)務需求實現(xiàn),例如:  
    // userService.updateById(convertToEntity(user));  
}  
@Override  
public void deleteUser(String username) {  
    userService.lambdaUpdate().eq(User::getUsername, username).remove();  
}  
@Override  
public void changePassword(String oldPassword, String newPassword) {  
}  
@Override  
public boolean userExists(String username) {  
    return userService.lambdaQuery().eq(User::getUsername, username).exists();  
}

3.UserController中實現(xiàn)接口并調(diào)用已重寫的方法

@RequiredArgsConstructor  
@RestController  
@RequestMapping("/user")  
public class UserController {  
    private final UserService userService;  
    private final UserDetailsManager userDetailsManager;  
    private final PasswordEncoder passwordEncoder;  
    @PostMapping("/add")  
    public String add(@RequestBody User user) {  
        // 1. 校驗用戶是否存在  
        if (userDetailsManager.userExists(user.getUsername())) {  
            return "添加失敗,用戶名已存在";  
        }  
        // 2. 加密密碼  
        user.setPassword(passwordEncoder.encode(user.getPassword()));  
        // 3. 使用自定義的 SecurityUser 包裝實體類  
        SecurityUser securityUser = new SecurityUser(user);  
        // 4. 通過標準接口保存用戶  
        try {  
            userDetailsManager.createUser(securityUser);  
            return "添加成功";  
        } catch (Exception e) {  
            log.error("添加用戶失敗", e);  
            return "添加失敗:" + e.getMessage();  
        }  
    }  
}

前后端響應

在用戶登錄成功,失敗或是注銷時返回給前端對應的JSON數(shù)據(jù)(此處為了方便演示,把統(tǒng)一響應結(jié)果封裝在Map集合中,實際開發(fā)還是以自定義結(jié)果對象為好)

登錄成功

編寫實現(xiàn)了AuthenticationSuccessHandler的類,用戶登錄成功時會調(diào)用該類中的onAuthenticationSuccess方法

@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        //獲取用戶身份信息
        Object principal = authentication.getPrincipal();
        //創(chuàng)建結(jié)果對象
        HashMap result = new HashMap();
        result.put("code", 0);
        result.put("message", "登錄成功");
        result.put("data", principal);
        //轉(zhuǎn)換成json字符串
        String json = JSON.toJSONString(result);
        //返回響應
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(json);
    }
}

登陸失敗

編寫實現(xiàn)了AuthenticationFailureHandler的類,用戶登錄失敗時會調(diào)用該類中的onAuthenticationFailure方法

@Component
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        //獲取錯誤信息
        String localizedMessage = exception.getLocalizedMessage();
        //創(chuàng)建結(jié)果對象
        HashMap result = new HashMap();
        result.put("code", -1);
        result.put("message", localizedMessage);
        //轉(zhuǎn)換成json字符串
        String json = JSON.toJSONString(result);
        //返回響應
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(json);
    }
}

注銷

編寫實現(xiàn)了LogoutSuccessHandler的類,用戶注銷時會調(diào)用該類中的onLogoutSuccess方法

@Component
public class MyLogoutSuccessHandler implements LogoutSuccessHandler {
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        //創(chuàng)建結(jié)果對象
        HashMap result = new HashMap();
        result.put("code", 0);
        result.put("message", "注銷成功");
        //轉(zhuǎn)換成json字符串
        String json = JSON.toJSONString(result);
        //返回響應
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(json);
    }
}

掛載

目前位置以上三個handler只是進行了實現(xiàn),但是為被掛載使用,掛載方法:

@Configuration  
@EnableMethodSecurity // 開啟方法級權(quán)限控制  
public class WebSecurityConfig {  
    private final MyAuthenticationSuccessHandler successHandler;  
    private final MyLogoutSuccessHandler logoutSuccessHandler;  
    private final MyAuthenticationFailureHandler failureHandler;  
    // 構(gòu)造器注入  
    public WebSecurityConfig(MyAuthenticationSuccessHandler successHandler, MyLogoutSuccessHandler logoutSuccessHandler, MyAuthenticationFailureHandler failureHandler) {  
        this.successHandler = successHandler;  
        this.logoutSuccessHandler = logoutSuccessHandler;  
        this.failureHandler = failureHandler;  
    }  
    @Bean  
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {  
        http.formLogin(form -> form  
                .successHandler(successHandler) // 啟用成功處理器  
                .failureHandler(failureHandler) // 啟用失敗處理器(
        );  
        http.logout(logout -> logout  
                .logoutSuccessHandler(logoutSuccessHandler) // 啟用注銷處理器
                .invalidateHttpSession(true) // 銷毀 Session
        );  
        return http.build();  
    }  
}

先通過構(gòu)造器注入handler,再在http構(gòu)造時對應的lambda表達式處掛載。

跨域

跨域全稱是跨域資源共享(Cross-Origin Resources Sharing,CORS),它是瀏覽器的保護機制,只允許網(wǎng)頁請求統(tǒng)一域名下的服務,同一域名指=>協(xié)議、域名、端口號都要保持一致,如果有一項不同,那么就是跨域請求。在前后端分離的項目中,需要解決跨域的問題。

在SpringSecurity中解決跨域很簡單,在配置文件中添加如下配置即可

//跨域
http.cors(withDefaults());

身份認證

基本概念

在Spring Security框架中,SecurityContextHolder、SecurityContext、Authentication、Principal和Credential是一些與身份驗證和授權(quán)相關的重要概念。它們之間的關系如下:

  1. SecurityContextHolder:SecurityContextHolder 是 Spring Security 存儲已認證用戶詳細信息的地方。
  2. SecurityContext:SecurityContext 是從 SecurityContextHolder 獲取的內(nèi)容,包含當前已認證用戶的 Authentication 信息。
  3. Authentication:Authentication 表示用戶的身份認證信息。它包含了用戶的Principal、Credential和Authority信息。
  4. Principal:表示用戶的身份標識。它通常是一個表示用戶的實體對象,例如用戶名。Principal可以通過Authentication對象的getPrincipal()方法獲取。
  5. Credentials:表示用戶的憑證信息,例如密碼、證書或其他認證憑據(jù)。Credential可以通過Authentication對象的getCredentials()方法獲取。
  6. GrantedAuthority:表示用戶被授予的權(quán)限

總結(jié)起來,SecurityContextHolder用于管理當前線程的安全上下文,存儲已認證用戶的詳細信息,其中包含了SecurityContext對象,該對象包含了Authentication對象,后者表示用戶的身份驗證信息,包括Principal(用戶的身份標識)和Credential(用戶的憑證信息)。

在Controller中獲取用戶信息

IndexController:

@RestController  
public class IndexController {  
    /**  
     * 獲取當前登錄用戶的詳細信息  
     * 用于學習 SecurityContextHolder 相關 API  
     */    @GetMapping("/user/info")  
    public String getUserInfo() {  
        // 1. 從 SecurityContextHolder 中獲取認證對象  
        // SecurityContextHolder 是一個工具類,內(nèi)部通過 ThreadLocal 存儲了當前線程的認證信息  
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();  
        // 2. 獲取用戶身份主體 (在你的項目中,你自定義的 SecurityUser )  
        Object principal = authentication.getPrincipal();  
        // 3. 獲取用戶權(quán)限信息 (如角色 ROLE_ADMIN 等,均為自定義)  
        Object authorities = authentication.getAuthorities();  
        // 4. 獲取認證狀態(tài) (是否已登錄)  
        boolean isAuthenticated = authentication.isAuthenticated();  
        // 為了方便查看,我們將這些信息封裝到 Map 中返回  
        Map<String, Object> info = new java.util.HashMap<>();  
        info.put("principal", principal);  
        info.put("authorities", authorities);  
        info.put("isAuthenticated", isAuthenticated);  
        info.put("name", authentication.getName()); // 獲取用戶名  
        // 使用 Fastjson2 手動轉(zhuǎn)換為 JSON 字符串  
        return JSON.toJSONString(info);  
    }  
}

會話并發(fā)處理

簡單來說就是,后登錄的賬號會使先登錄的賬號失效。

實現(xiàn)處理器接口SessionInformationExpiredStrategy

@Component  
public class MySessionInformationExpiredStrategy implements SessionInformationExpiredStrategy {  
    @Override  
    public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {  
        // 1. 獲取響應對象  
        HttpServletResponse response = event.getResponse();  
        // 2. 創(chuàng)建你要返回的 JSON 結(jié)果  
        Map<String, Object> result = new HashMap<>();  
        result.put("code", 401);  
        result.put("message", "您的賬號已在其他地方登錄,當前會話已失效");  
        // 3. 將結(jié)果轉(zhuǎn)換為 JSON 并寫入響應  
        response.setContentType("application/json;charset=UTF-8");  
        response.getWriter().println(JSON.toJSONString(result));  
    }  
}

config中掛載配置

http.sessionManagement(session -> session  
                .maximumSessions(1) // 限制同一個賬號只能 1 個登錄  
                .expiredSessionStrategy(sessionInformationExpiredStrategy) // 掛載自定義策略  
        );

到此這篇關于SpringSecurity入門實戰(zhàn)應用場景的文章就介紹到這了,更多相關SpringSecurity登錄認證內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Mybatis圖文并茂講解分頁插件

    Mybatis圖文并茂講解分頁插件

    使用過mybatis的人都知道,mybatis本身就很小且簡單,sql寫在xml里,統(tǒng)一管理和優(yōu)化。缺點當然也有,比如我們使用過程中,要使用到分頁,如果用最原始的方式的話,1.查詢分頁數(shù)據(jù),2.獲取分頁長度,也就是說要使用到兩個方法才能完成分頁
    2022-07-07
  • Java Socket編程筆記_動力節(jié)點Java學院整理

    Java Socket編程筆記_動力節(jié)點Java學院整理

    Socket對于我們來說就非常實用了。下面是本次學習的筆記。主要分異常類型、交互原理、Socket、ServerSocket、多線程這幾個方面闡述
    2017-05-05
  • Java應用EasyExcel工具類

    Java應用EasyExcel工具類

    這篇文章主要介紹了Java應用EasyExcel工具類,文中有非常詳細的代碼示例,對正在學習java的小伙伴們有一定的幫助,需要的朋友可以參考下
    2021-05-05
  • Java實現(xiàn)本地緩存的四種方案(Guava Cache、Caffeine、Ehcach 和Spring Cache)

    Java實現(xiàn)本地緩存的四種方案(Guava Cache、Caffeine、Ehcach 和Sprin

    在現(xiàn)代應用程序開發(fā)中,緩存是提高性能和響應速度的關鍵技術(shù)之一,Java 提供了多種本地緩存解決方案,每種方案都有其特點和適用場景,本文將介紹四種常見的 Java 本地緩存實現(xiàn),需要的朋友可以參考下
    2024-12-12
  • SpringBoot項目中resources文件讀取實踐

    SpringBoot項目中resources文件讀取實踐

    本文詳細介紹了SpringBoot項目中讀取resources目錄下文件的9種主流方式,并提供了一個完整的控制器Demo示例,幫助開發(fā)者快速定位最適合的資源加載方案
    2026-01-01
  • SpringBoot中實現(xiàn)代理方式

    SpringBoot中實現(xiàn)代理方式

    這篇文章主要介紹了SpringBoot中實現(xiàn)代理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • java?list和map切割分段的實現(xiàn)及多線程應用案例

    java?list和map切割分段的實現(xiàn)及多線程應用案例

    這篇文章主要為大家介紹了java?list和map切割分段的實現(xiàn)及多線程應用案例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-12-12
  • Java?Swing自定義復選框CusCheckBox組件(附源碼)

    Java?Swing自定義復選框CusCheckBox組件(附源碼)

    Swing 原生 JCheckBox 的復選框圖標是系統(tǒng)默認樣式,不同操作系統(tǒng)下風格不一致,且無法自定義圓角、顏色、懸停效果等, 下面我們就用Java Swing實現(xiàn)自定義的復選框組件吧
    2026-05-05
  • Java實現(xiàn)url加密處理的方法示例

    Java實現(xiàn)url加密處理的方法示例

    這篇文章主要介紹了Java實現(xiàn)url加密處理的方法,涉及java基于base64、編碼轉(zhuǎn)換實現(xiàn)加密解密相關操作技巧,需要的朋友可以參考下
    2017-06-06
  • Mybatis中BindingException異常的產(chǎn)生原因及解決過程

    Mybatis中BindingException異常的產(chǎn)生原因及解決過程

    BindingException異常是MyBatis框架中自定義的異常,顧名思義指的是綁定出現(xiàn)問題,下面這篇文章主要給大家介紹了關于MyBatis報錯BindingException異常的產(chǎn)生原因及解決過程,需要的朋友可以參考下
    2023-06-06

最新評論

涟源市| 法库县| 新乐市| 遂宁市| 阿拉善右旗| 桂东县| 从化市| 广东省| 西华县| 正安县| 荥阳市| 綦江县| 胶南市| 洛阳市| 舒城县| 利川市| 玉溪市| 怀宁县| 儋州市| 于都县| 石渠县| 邵武市| 镇雄县| 平舆县| 达州市| 浦江县| 子洲县| 舟曲县| 额济纳旗| 逊克县| 尖扎县| 崇文区| 崇左市| 托克逊县| 分宜县| 隆昌县| 濮阳县| 潜山县| 南皮县| 泗阳县| 临沭县|