java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Spring Security入门

Spring Security入门到深入实战步骤详解

作者:佛祖让我来巡山

小明通过学习SpringSecurity,成功为自己的摄影网站添加了完整的登录认证鉴权功能,包括自定义用户存储、权限控制、自定义登录页、JWT集成以及OAuth2第三方登录,并且对SpringSecurity有了深入的理解和认识,喜欢的朋友跟随小编一起学习吧

小 明 的 摄 影 网 站 已 经 用 Spring Boot 搭 建 完 成 , 访 客 越 来 越 多 。 他 决 定 给 网 站 加 上 完 整 的 登 录 认 证 鉴 权 功 能 , 让 不 同 角 色 的 用 户 ( 游 客 、 编 辑 、 管 理 员 ) 有 不 同 的 操 作 权 限 。 今 天 , 他 就 带 着 这 个 需 求 , 开 启 Spring Security 的 学 习 之 旅 。

** 第 一 步 : 搭 建 Spring Boot 项 目 , 引 入 Spring Security **

** 小 明 的 需 求 **

“ 先 让 网 站 有 个 最 基 础 的 登 录 功 能 , 能 拦 住 未 登 录 的 用 户 。 ”

** 实 操 步 骤 **

<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>  <!--  Spring Security  依  赖  -->  
    </dependency>  
    <dependency>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-starter-data-jpa</artifactId>  
    </dependency>  
    <dependency>  
        <groupId>com.mysql</groupId>  
        <artifactId>mysql-connector-j</artifactId>  
    </dependency>  
</dependencies>  

** 现 象 与 原 理 **

💡 小 明 的 疑 问 : “ 这 密 码 每 次 启 动 都 变 , 而 且 用 户 不 能 自 己 注 册 , 得 改 ! ”

** 第 二 步 : 自 定 义 用 户 存 储 —— 从 内 存 到 数 据 库 **

** 小 明 的 需 求 **

“ 用 户 信 息 存 在 自 己 的 数 据 库 里 , 支 持 注 册 、 登 录 , 密 码 用 BCrypt 加 密 。 ”

** 核 心 概 念 **

** 实 操 步 骤 **

** 建 用 户 表 ** ( MySQL ) :

CREATE TABLE users (  
    id BIGINT AUTO_INCREMENT PRIMARY KEY,  
    username VARCHAR(50) UNIQUE NOT NULL,  --  用  户  名  
    password VARCHAR(100) NOT NULL,         --  BCrypt  哈  希  后  的  密  码  
    role VARCHAR(20) NOT NULL               --  角  色  :  ROLE_VISITOR  /  ROLE_EDITOR  /  ROLE_ADMIN  
);  

** 实 现 UserDetailsService ** ( 查 数 据 库 用 户 ) :

@Service  
public class CustomUserDetailsService implements UserDetailsService {  
    @Autowired private UserRepository userRepo;  //  JPA  操  作  users  表  
    @Override  
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {  
        //  1.  查  数  据  库  用  户  
        User user = userRepo.findByUsername(username)  
            .orElseThrow(() -> new UsernameNotFoundException("用户不存在"));  
        //  2.  封  装  成  UserDetails  对  象  (  Spring Security  认  识  的  格  式  )  
        return org.springframework.security.core.userdetails.User.builder()  
            .username(user.getUsername())  
            .password(user.getPassword())  //  存  的  是  BCrypt  哈  希  值  
            .roles(user.getRole().replace("ROLE_", ""))  //  角  色  去  掉  ROLE_  前  缀  
            .build();  
    }  
}  

** 配 置 PasswordEncoder ** ( 用 BCrypt 加 密 ) :

@Configuration  
public class SecurityConfig {  
    @Bean  
    public PasswordEncoder passwordEncoder() {  
        return new BCryptPasswordEncoder();  //  BCrypt  加  密  器  ,  自  带  随  机  盐  
    }  
}  

** 注 册 用 户 ** ( 比 如 小 明 自 己 注 册 为 管 理 员 ) :

@RestController  
public class RegisterController {  
    @Autowired private UserRepository userRepo;  
    @Autowired private PasswordEncoder passwordEncoder;  
    @PostMapping("/register")  
    public String register(String username, String rawPassword, String role) {  
        User user = new User();  
        user.setUsername(username);  
        user.setPassword(passwordEncoder.encode(rawPassword));  //  明  文  密  码  →  BCrypt  哈  希  
        user.setRole("ROLE_" + role.toUpperCase());  //  角  色  加  ROLE_  前  缀  (  Spring Security  约  定  )  
        userRepo.save(user);  
        return "注册成功!";  
    }  
}  

** 效 果 **

小 明 用 POST /register?username=xiaoming&rawPassword=123456&role=admin 注 册 后 , 数 据 库 users 表 会 存 一 条 记 录 , password 字 段 是 BCrypt 哈 希 值 ( 如 $2a$10$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx )。 登 录 时 , 框 架 会 用 BCrypt 验 证 输 入 密 码 是 否 匹 配 。

💡 小 明 的 体 会 : “ UserDetailsService 就 像 图 书 馆 的 ‘ 查 阅 员 ’ , 框 架 告 诉 他 ‘ 找 小 明 ’ , 他 就 去 数 据 库 把 小 明 的 书 ( 用 户 信 息 ) 拿 回 来 。 ”

** 第 三 步 : 配 置 权 限 控 制 —— 谁 能 干 啥 ? **

** 小 明 的 需 求 **

“ 游 客 只 能 看 照 片 , 编 辑 能 上 传 , 管 理 员 能 删 除 。 ”

** 核 心 概 念 **

** 实 操 步 骤 **

** 配 置 URL 访 问 权 限 ** ( 在 SecurityConfig 中 ) :

@Configuration  
@EnableWebSecurity  //  开  启  Web  安  全  配  置  
public class SecurityConfig {  
    @Autowired private CustomUserDetailsService userDetailsService;  
    @Autowired private PasswordEncoder passwordEncoder;  

    @Bean  
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {  
        http  
            .authorizeHttpRequests(auth -&gt; auth  
                .requestMatchers("/photos/**").permitAll()  //  公  开  :  所  有  人  能  看  照  片  
                .requestMatchers("/upload/**").hasRole("EDITOR")  //  上  传  :  需  EDITOR  角  色  
                .requestMatchers("/delete/**").hasRole("ADMIN")  //  删  除  :  需  ADMIN  角  色  
                .anyRequest().authenticated()  //  其  他  接  口  需  登  录  
            )  
            .formLogin(form -&gt; form  //  配  置  登  录  页  
                .loginPage("/my-login")  //  自  定  义  登  录  页  (  下  节  讲  )  
                .defaultSuccessUrl("/home")  //  登  录  成  功  跳  转  
            )  
            .logout(logout -&gt; logout  //  配  置  注  销  
                .logoutUrl("/logout")  
                .logoutSuccessUrl("/photos")  
            );  
        return http.build();  
    }  

    //  配  置  认  证  管  理  器  (  用  自  定  义  UserDetailsService  和  PasswordEncoder  )  
    @Bean  
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {  
        return config.getAuthenticationManager();  
    }  
}  

** 用 @PreAuthorize 做 方 法 级 权 限 ** ( 更 精 细 控 制 ) :

@RestController  
public class PhotoController {  
    //  游  客  能  看  
    @GetMapping("/photos")  
    public List<Photo> listPhotos() { ... }  
    //  编  辑  能  上  传  (  @PreAuthorize  需  开  启  @EnableMethodSecurity  )  
    @PostMapping("/upload")  
    @PreAuthorize("hasRole('EDITOR')")  //  直  接  标  注  需  EDITOR  角  色  
    public String uploadPhoto(MultipartFile file) { ... }  
    //  管  理  员  能  删  除  
    @DeleteMapping("/delete/{id}")  
    @PreAuthorize("hasRole('ADMIN') or hasRole('EDITOR')")  //  管  理  员  或  编  辑  都  能  删  
    public String deletePhoto(@PathVariable Long id) { ... }  
}  

** 效 果 **

💡 小 明 的 体 会 : “ HttpSecurity 像 ‘ 门 卫 ’ , 管 URL 级 的 大 门 ; @PreAuthorize 像 ‘ 房 间 锁 ’ , 管 方 法 级 的 小 门 , 双 保 险 ! ”

** 第 四 步 : 自 定 义 登 录 页 、 注 销 与 记 住 我 **

** 小 明 的 需 求 **

“ 默 认 登 录 页 太 丑 , 想 用 自 己 的 ; 加 个 ‘ 记 住 我 ’ 功 能 , 关 闭 浏 览 器 再 打 开 还 是 登 录 状 态 。 ”

** 实 操 步 骤 **

** 效 果 **

💡 小 明 的 体 会 : “ 自 定 义 登 录 页 让 网 站 更 好 看 , ‘ 记 住 我 ’ 像 ‘ 长 期 饭 卡 ’ , 不 用 每 次 都 刷 临 时 卡 。 ”

** 第 五 步 : 集 成 JWT —— 支 持 APP 与 微 服 务 **

** 小 明 的 需 求 **

“ 我 做 了 个 手 机 APP , 用 Session - Cookie 不 方 便 , 想 用 JWT Token 做 认 证 。 ”

** 核 心 概 念 **

** 实 操 步 骤 **

** 效 果 **

💡 小 明 的 体 会 : “ JWT 像 ‘ 电 子 通 行 证 ’ , APP 拿 着 它 就 能 畅 通 无 阻 , 服 务 器 不 用 记 住 谁 来 过 , 轻 松 支 持 多 端 ! ”

** 第 六 步 : 深 入 : OAuth2 第 三 方 登 录 与 安 全 防 护 **

** 小 明 的 需 求 **

“ 想 让 用 户 用 GitHub 账 号 直 接 登 录 , 还 要 防 CSRF 、 XSS 攻 击 。 ”

** 核 心 概 念 **

** 实 操 步 骤 **

** 效 果 **

💡 小 明 的 体 会 : “ OAuth2 像 ‘ 找 朋 友 作 证 ’ , 让 GitHub 帮 忙 证 明 ‘ 这 是 小 明 ’ ; 框 架 的 安 全 防 护 像 ‘ 隐 形 铠 甲 ’ , 平 时 感 觉 不 到 , 关 键 时 刻 能 挡 刀 。 ”

** 小 明 的 学 习 总 结 **

通 过 这 几 步 , 小 明 彻 底 掌 握 了 Spring Security 配 Spring Boot 的 核 心 用 法 :

✨ 小 明 的 最 终 感 悟 :
Spring Security 不 是 一 堆 复 杂 的 配 置 , 而 是 一 个 “ 安 全 积 木 盒 ” 。 小 明 用 它 搭 建 了 摄 影 网 站 的 安 全 堡 垒 , 从 默 认 配 置 到 自 定 义 扩 展 , 从 Web 到 APP , 从 账 号 密 码 到 第 三 方 登 录 , 每 一 步 都 踩 在 “ 需 求 ” 和 “ 技 术 ” 的 结 合 点 上 。 现 在 , 他 可 以 专 心 拍 摄 更 美 的 照 片 , 而 网 站 的 安 全 , 交 给 Spring Security 就 够 了 !

到此这篇关于Spring Security入门到深入实战步骤详解的文章就介绍到这了,更多相关Spring Security入门内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文