IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SpringSecurity 配置与使用(含新 API 替换过时的 WebSecurityConfigurerAdapter) -> 正文阅读

[Java知识库]SpringSecurity 配置与使用(含新 API 替换过时的 WebSecurityConfigurerAdapter)


前言

As we all know,现今主流权限框架有 SpringSecurity、Shiro、SaToken,Shiro在前后端分离时代基本被淘汰,剩下适合大型项目的 SpringSecurity 和 适合中小型项目的 SaToken 可以选择,SaToken 我也写了文章 Springboot 使用 SaToken 进行登录认证、权限管理以及路由规则接口拦截

SpringSecurity 作为 Spring 的官方权限框架,肯定是最牛逼的,当然也最复杂,中小型项目还是 SaToken 来的省心呀,简单,几行代码实现认证、拦截、踢人、单点登录等,SpringSecurity 想要实现这些功能,需要深入研究,现在我只写最简单的用户认证和接口权限控制。

现在的 SpringSecurity 版本更换了新的配置方式,下面有写


一、引入依赖

自己新建一个标准的 Springboot web 项目,然后增加下面这个依赖

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

二、启动类增加注解

@EnableWebSecurity表示启用户 springsecurity 功能
@EnableGlobalMethodSecurity(prePostEnabled = true)是开启基于注解的接口权限控制

/**
 * @author 徐一杰
 * @date 2022/10/25 17:18
 * 开启方法级安全验证
 */
@SpringBootApplication
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityDemoApplication {

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

}

三、config配置文件

现在的 SpringSecurity 版本更换了新的配置方式,目前新版本仍兼容旧版配置,你不喜欢新版配置也可以用旧版

旧版配置

import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @author 徐一杰
 * @date 2022/10/25 15:28
 * 这是旧版api
 */
@SpringBootConfiguration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 指定加密方式
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        // 使用官方推荐的BCrypt加密密码,也就是md5加随机盐
        return new BCryptPasswordEncoder();
    }

    /**
     * configure(WebSecurity)用于影响全局安全性(配置资源,设置调试模式,通过实现自定义防火墙定义拒绝请求)的配置设置。
     * 一般用于配置全局的某些通用事物,例如静态资源等
     * @param web 
     * @throws Exception
     */
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/resources/**", "/ignore2");
    }

    /**
     * 配置接口拦截
     * configure(HttpSecurity)允许基于选择匹配在资源级配置基于网络的安全性,也就是对角色所能访问的接口做出限制
     * @param httpSecurity 请求属性
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.authorizeRequests()
                // 允许get请求/test/any,而无需认证,不配置HttpMethod默认允许所有请求类型
                .antMatchers(HttpMethod.GET, "/test/any").permitAll()
                //指定权限为admin才能访问,这里和方法注解配置效果一样,但是会覆盖注解
                .antMatchers("/test/admin").hasRole("admin")
                // 所有请求都需要验证
                .anyRequest().authenticated()
                .and()
                //.httpBasic() Basic认证,和表单认证只能选一个
                // 使用表单认证页面
                .formLogin()
                //配置登录入口,默认为security自带的页面/login
                .loginProcessingUrl("/login")
                .and()
                // post请求要关闭csrf验证,不然访问报错;实际开发中开启,需要前端配合传递其他参数
                .csrf().disable();
    }
}


新版配置

package icu.xuyijie.springsecuritydemo.config;

import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

/**
 * @author 徐一杰
 * @date 2022/10/25 17:34
 * 新api
 */
@SpringBootConfiguration
public class WebSecurityNewConfig {

    /**
     * 指定加密方式
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        // 使用官方推荐的BCrypt加密密码,也就是md5加随机盐
        return new BCryptPasswordEncoder();
    }

    /**
     * configure(WebSecurity)用于影响全局安全性(配置资源,设置调试模式,通过实现自定义防火墙定义拒绝请求)的配置设置。
     * 一般用于配置全局的某些通用事物,例如静态资源等
     * 新版本其实不推荐把路径的拦截写在这里,而是推荐写在securityFilterChain里面
     * "You are asking Spring Security to ignore Ant [pattern='/resources/**']. This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead"
     */
    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().antMatchers("/resources/**", "/ignore2");
    }

    /**
     * 配置接口拦截
     * configure(HttpSecurity)允许基于选择匹配在资源级配置基于网络的安全性,也就是对角色所能访问的接口做出限制
     * @param httpSecurity 请求属性
     * @return HttpSecurity
     * @throws Exception
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.authorizeRequests()
                // 允许get请求/test/any,而无需认证,不配置HttpMethod默认允许所有请求类型
                .antMatchers(HttpMethod.GET, "/test/any", "/js/**", "/css/**", "/images/**", "/icon/**", "/file/**").permitAll()
                // 指定权限为admin才能访问,这里和方法注解配置效果一样,但是会覆盖注解
                .antMatchers("/test/admin").hasRole("admin")
                // 所有请求都需要验证
                .anyRequest().authenticated()
                .and()
                //.httpBasic() Basic认证,和表单认证只能选一个
                // 使用表单认证页面
                .formLogin()
                // 配置登录入口,默认为security自带的页面/login
                .loginProcessingUrl("/login")
                .and()
                // post请求要关闭csrf验证,不然访问报错;实际开发中开启,需要前端配合传递其他参数
                .csrf().disable();
        return httpSecurity.build();
    }
}


四、UserDetailsServiceImpl

UserDetailsService 是 SpringSecurity 的内置类,我们需要实现它的 loadUserByUsername 方法,方法参数 username 就是登录时填写的用户名,里面写从数据库获取这个 username 的密码和角色,然后 return 给 SpringSecurity 内置的 User 实体类

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * @author 徐一杰
 * @date 2022/10/25 15:22
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
	/**
     * 注入SpringSecurity内置的密码加密类
     */
	@Autowired
    private final PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 通过用户名从数据库获取用户信息,略,我们直接手动定义个用户
        // 定义用户角色为 user
        String role = "user";
        // 定义用户密码
        String password = "123456";
        // 角色列表
        List<GrantedAuthority> authorityList = new ArrayList<>();
        // 角色必须以ROLE_开头,security在判断角色时会自动截取ROLE_
        authorityList.add(new SimpleGrantedAuthority("ROLE_" + role));

        return new User(
                username,
                // springsecurity对比密码时默认会解密密码后对比,因为数据库一般存取加密后的密码,我们这里是明文,所以需加密密码
                passwordEncoder.encode(password),
                authorityList
        );
    }
}


五、写一个Controller测试用

上面主启动类添加的注解开启基于注解的接口权限的意思就是开启下面的 @PreAuthorize注解的功能,
这个注解和config 里面配置的 .antMatchers("/test/admin").hasRole("admin") 一个意思,选其中一个方式即可。

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 徐一杰
 * @date 2022/10/25 16:28
 */
@RestController
@RequestMapping("/test")
public class TestController {

    /**
     * user和admin角色能访问该方法
     */
    @PreAuthorize("hasAnyRole('user', 'admin')")
    @RequestMapping("/user")
    public String user(){
        System.out.println("user和admin角色访问");
        return "user和admin角色访问";
    }

    /**
     * admin角色才能访问该方法
     */
    @PreAuthorize("hasAnyRole('admin')")
    @RequestMapping("/admin")
    public String admin(){
        System.out.println("admin角色访问");
        return "admin角色访问";
    }

    /**
     * WebSecurityConfig里配置了该接口Get请求无需登录
     */
    @RequestMapping("/any")
    public String any(){
        System.out.println("这个接口Get请求无需登录");
        return "这个接口Get请求无需登录";
    }

}


六、启动测试

我们先访问 http://127.0.0.1:8081/test/any,这时我们还没有登录,这个接口我们在config 里面配置了.permitAll(),所以没有被拦截,直接访问成功

在这里插入图片描述

下面我门访问 http://127.0.0.1:8081/test/user,发现浏览器自动跳转到了登录界面,这个登录界面是SpringSecurity内置的,如需使用自定义页面,下面会讲

在这里插入图片描述

输入账号密码点击 Sign in,发现浏览器自动跳回http://127.0.0.1:8081/test/user,访问成功
这里我们用户名可以随便输入,因为上面UserDetailsServiceImpl中我们没有指定用户名,所以 123456 这个密码所有用户都能用

在这里插入图片描述

在这里插入图片描述

如果访问 /test/admin 这个接口,报错 403,代表无权限

在这里插入图片描述


七、前后端分离设计

1、自定义登录界面

config里面配置的 .loginProcessingUrl("/login")是默认使用SpringSecurity内置登录页面,如果需要使用前端登陆页面,可以配置一个 MvnConfig 拦截接口,让前端跳转到他们的登录页面,然后把登录请求发送给/login这个内置接口就行了

当然也可以单独写一个登录页面放到后端的 resources/static里面,这样可以直接在 config的loginProcessingUrl中修改

2、自定义登录成功/失败处理器

自定义处理器,这里写你登录失败的逻辑,返回给前端数据,让前端进行页面跳转

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;

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

/**
 * @author 徐一杰
 * @date 2022/10/25 17:19
 * security认证失败逻辑,定义这个以后,security内置的页面跳转就失效了
 */
@Component
public class LoginFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        response.setContentType("application/json;charset=UTF-8");
        System.out.println("登录成功");
        //这里写你登录失败的逻辑,返回给前端数据,让前端进行页面跳转
    }
}

configsecurityFilterChain方法里面添加下面代码,loginSuccessHandler代码和上面一样

// 使用自定义的结果处理器,定义这个以后,security内置的页面跳转就失效了
      .successHandler(loginSuccessHandler)
      .failureHandler(loginFailureHandler)

总结

SpringSecurity 作为 Spring 的官方权限框架,肯定是最牛逼的,当然也最复杂,中小型项目还是 SaToken 来的省心呀,简单,几行代码实现认证、拦截、踢人、单点登录等,SpringSecurity 想要实现这些功能,SaToken 我也写了文章 Springboot 使用 SaToken 进行登录认证、权限管理以及路由规则接口拦截

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-10-31 11:40:47  更:2022-10-31 11:45:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 4:58:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码