SpringBoot整合SpringSecurity实现认证拦截的教程

首先,我们需要确保具备以下的环境:

  • JDK 1.8+
  • Maven
  • IntelliJ IDEA(或其他IDE)

接下来,我们可以按照以下步骤进行SpringBoot整合SpringSecurity实现认证拦截:

步骤一:创建SpringBoot工程

我们可以使用SpringBoot官方提供的Spring Initializr来创建工程,也可以使用IDEA的New Project功能创建工程。

步骤二:添加依赖

我们需要在pom.xml文件中添加以下依赖:

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

步骤三:配置Spring Security

我们需要在SpringBoot工程中添加一个SecurityConfig类来配置Spring Security:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/", "/home").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
                .logout()
                .permitAll();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("user").password(passwordEncoder().encode("password")).roles("USER");
    }

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

以上是一个简单的Spring Security配置,其中:

  • configure(HttpSecurity http)方法配置了访问控制规则,首页和/home页面可以访问,其他页面需要认证之后才能访问;
  • configureGlobal(AuthenticationManagerBuilder auth)方法配置了用户名和密码,这里我们使用了内存认证方式;
  • passwordEncoder()方法配置了密码加密方式,这里我们使用了BCryptPasswordEncoder加密方式。

步骤四:添加登录页面和处理登录请求的Controller

我们需要添加一个登录页面和一个Controller来处理登录请求,代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login Page</title>
</head>
<body>
    <form th:action="@{/login}" method="post">
        <div>
            <label>Username</label>
            <input type="text" name="username"/>
        </div>
        <div>
            <label>Password</label>
            <input type="password" name="password"/>
        </div>
        <div>
            <button type="submit">Sign in</button>
        </div>
    </form>
</body>
</html>
@Controller
public class LoginController {

    @GetMapping("/login")
    public String login() {
        return "login";
    }

    @PostMapping("/login")
    public String doLogin() {
        return "redirect:/index";
    }

    @GetMapping("/index")
    public String index() {
        return "index";
    }
}

以上代码中,login.html是登录页面,LoginController是处理登录请求的Controller,doLogin()方法返回redirect:/index,当登录成功后会跳转到/index页面。

现在,我们可以启动SpringBoot工程,访问http://localhost:8080/home页面,可以看到Spring Security的默认登录页面,输入用户名user和密码password,登录成功后会跳转到/index页面。

这是一个简单的实现过程,接下来我们将会使用两个示例来进一步展示SpringBoot整合SpringSecurity实现认证拦截。

示例一:基于数据库认证

我们将会使用MySQL数据库来存储用户信息和角色信息,并通过JdbcUserDetailsManager来实现用户认证。

首先,我们需要创建一个数据库和表:

CREATE DATABASE spring_security_demo;

USE spring_security_demo;

CREATE TABLE users (
  id INT PRIMARY KEY AUTO_INCREMENT,
  username VARCHAR(50) NOT NULL UNIQUE,
  password VARCHAR(100) NOT NULL
);

CREATE TABLE roles (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(50) NOT NULL
);

CREATE TABLE user_roles (
  user_id INT,
  role_id INT,
  PRIMARY KEY (user_id, role_id),
  FOREIGN KEY (user_id) REFERENCES users(id),
  FOREIGN KEY (role_id) REFERENCES roles(id)
);

然后,我们需要添加依赖:

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

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

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>${mysql.version}</version>
</dependency>

其中,${mysql.version}是MySQL JDBC驱动的版本号。

接下来,我们需要在application.properties文件中添加数据库连接和认证相关的配置:

spring.datasource.url=jdbc:mysql://localhost:3306/spring_security_demo
spring.datasource.username=root
spring.datasource.password=123456

spring.security.user.name=user
spring.security.user.password=password

需要注意的是,spring.security.user.namespring.security.user.password是Spring Security内存认证的默认配置,可以不设置。

然后,我们需要在SecurityConfig类中修改configureGlobal(AuthenticationManagerBuilder auth)方法,使用JdbcUserDetailsManager认证:

@Autowired
DataSource dataSource;

@Autowired
PasswordEncoder passwordEncoder;

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    JdbcUserDetailsManager userDetailsManager = new JdbcUserDetailsManager(dataSource);

    if (!userDetailsManager.userExists("user")) {
        userDetailsManager.createUser(
                User.withUsername("user")
                    .password(passwordEncoder.encode("password"))
                    .roles("USER")
                    .build());
    }

    auth.userDetailsService(userDetailsManager).passwordEncoder(passwordEncoder);
}

以上代码中,我们首先使用JdbcUserDetailsManagerDataSource进行初始化,然后判断是否已存在用户名为user的用户,如果不存在则创建该用户,最后使用userDetailsService()passwordEncoder()配置用户认证和密码加密方式。

现在,我们可以启动SpringBoot工程,访问http://localhost:8080/home页面,输入用户名user和密码password,登录成功后会跳转到/index页面。

示例二:基于JWT认证

我们将会使用JWT来实现用户认证,JWT是一种轻量级的跨域认证方案。

首先,我们需要添加依赖:

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

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>${jjwt.version}</version>
</dependency>

其中,${jjwt.version}是JJWT的版本号。

然后,我们需要在application.properties文件中添加JWT相关的配置:

spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://localhost:9090/oauth2/.well-known/jwks.json
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:9090/oauth2/token

jwt.secret=mysecret
jwt.expiration-in-ms=1800000

以上代码中,spring.security.oauth2.resourceserver.jwt.jwk-set-uri是JWT公钥的URL,spring.security.oauth2.resourceserver.jwt.issuer-uri是JWT发行者的URL,这些信息可从认证服务器获取;jwt.secret是JWT私钥,jwt.expiration-in-ms是JWT过期时间。

下面是单点登录的示例:

@Configuration
@EnableWebSecurity
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .csrf()
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
    }
}

以上代码中,@EnableOAuth2Sso注解即启用了单点登录。

接下来,我们需要创建一个JwtProvider类来生成和解析JWT:

@Component
public class JwtProvider {

    @Value("${jwt.secret}")
    private String jwtSecret;

    @Value("${jwt.expiration-in-ms}")
    private int jwtExpirationInMs;

    public String generateToken(Authentication authentication) {
        User principal = (User) authentication.getPrincipal();

        Date now = new Date();
        Date expiration = new Date(now.getTime() + jwtExpirationInMs);

        return Jwts.builder()
                .setSubject(principal.getUsername())
                .setIssuedAt(now)
                .setExpiration(expiration)
                .signWith(SignatureAlgorithm.HS512, jwtSecret)
                .compact();
    }

    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token);
            return true;
        } catch (SignatureException | MalformedJwtException | ExpiredJwtException | UnsupportedJwtException | IllegalArgumentException exception) {
            return false;
        }
    }

    public String getUsernameFromToken(String token) {
        Claims claims = Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody();
        return claims.getSubject();
    }
}

以上代码中,generateToken()方法用于生成JWT,validateToken()方法用于校验JWT是否有效,getUsernameFromToken()方法用于从JWT中获取用户名。

然后,我们需要添加一个JwtAuthenticationFilter类:

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtProvider jwtProvider;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        try {
            String jwt = getJwtFromRequest(request);

            if (StringUtils.hasText(jwt) && jwtProvider.validateToken(jwt)) {
                String username = jwtProvider.getUsernameFromToken(jwt);

                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        } catch (Exception ex) {
            logger.error("Could not set user authentication", ex);
        }

        filterChain.doFilter(request, response);
    }

    private String getJwtFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}

以上代码中,doFilterInternal()方法在每次请求前都会调用,用于对JWT进行校验,如果JWT有效则设置当前用户的认证状态;getJwtFromRequest()方法用于从HTTP请求头中获取JWT。

最后,我们需要在SecurityConfig类中添加JWT认证相关的配置:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    ...

    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter;

    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() {
        return new JwtAuthenticationFilter();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests().antMatchers("/login/**", "/signup/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

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

以上代码中,我们使用jwt前缀来标识JWT,将不需要进行认证的URL添加到permitAll()中,使用addFilterBefore()方法添加JwtAuthenticationFilter到Spring Security过滤链中,并将其设置在用户名密码认证过滤器之前。

现在,我们可以启动SpringBoot工程,访问http://localhost:8080/home页面,输入用户名user和密码password,登录成功后会生成一个JWT,将其携带在请求头中即可访问其他受保护的资源。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringBoot整合SpringSecurity实现认证拦截的教程 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • druid升级后sql监控页面为空白的解决

    针对“druid升级后sql监控页面为空白”的问题,以下是详细的解决攻略: 问题背景 在升级druid版本(例如从0.7.0版本升级到1.2.3版本)后,访问sql监控页面时可能出现页面完全空白的情况。 解决过程 步骤1:检查druid的properties配置 在druid的properties配置文件中,需要增加如下配置项: druid.stat.mer…

    Java 2023年6月16日
    00
  • spring retry实现方法请求重试的使用步骤

    下面我将详细讲解使用Spring Retry实现请求重试的使用步骤。 1. 引入Spring Retry 在Spring Boot中,我们可以通过在pom.xml中引入以下依赖来使用Spring Retry: <dependency> <groupId>org.springframework.retry</groupId>…

    Java 2023年5月20日
    00
  • 详解Java线程池是如何重复利用空闲线程的

    下面我就给你详细讲解“详解Java线程池是如何重复利用空闲线程的”的完整攻略。 1. 什么是Java线程池 Java线程池实际上是一种管理多线程的机制,它可以控制多线程的创建和销毁,以便更好地管理系统资源。线程池可以避免系统频繁地创建和销毁线程,从而降低系统的负担。 2. Java线程池如何重复利用空闲线程 Java线程池中有一组空闲线程,它们被称为“工作线…

    Java 2023年5月26日
    00
  • 一天吃透JVM面试八股文

    什么是JVM? JVM,全称Java Virtual Machine(Java虚拟机),是通过在实际的计算机上仿真模拟各种计算机功能来实现的。由一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域等组成。JVM屏蔽了与操作系统平台相关的信息,使得Java程序只需要生成在Java虚拟机上运行的目标代码(字节码),就可在多种平台上不加修改的运行,…

    Java 2023年4月19日
    00
  • 如何避免内存泄漏?

    以下是关于如何避免内存泄漏的完整使用攻略: 什么是内存泄漏? 内存泄漏是指在程序运行过程中,分配的内存空间没有被及时释放,导致内存空间的浪费和程序运行速度的下降。内存泄漏是一种常见的程序错误,如果不及时处理,会导致程序崩溃或者系统崩溃。 如何避免内存泄漏? 为了避免内存泄漏,需要注意以下几点: 1. 及时释放内存 在程序中,如果分配了内存空间,就需要在不需要…

    Java 2023年5月12日
    00
  • 浅谈java中异常抛出后代码是否会继续执行

    浅谈Java中异常抛出后代码是否会继续执行 什么是异常 在Java编程中,异常指的是一个事件,它会在程序执行期间发生,影响了程序正常的执行流程。在Java中,异常是一个对象,它是Throwable类或它的子类的实例。 比如在进行整型变量除以0的操作的时候就会抛出一个异常,这个时候程序不会顺着正常的执行流程走下去,而是会跳出目前的代码执行流,转而执行异常处理流…

    Java 2023年5月27日
    00
  • Java的后台文件夹下文件的遍历完整代码

    下面给您详细讲解Java后台文件夹下文件遍历的完整攻略。 一、文件夹遍历基本原理 首先需要一个File对象,用来表示文件夹或文件; 通过该File对象调用listFiles()方法获取该文件夹下的所有子文件或子文件夹; 遍历得到的子文件或子文件夹,如果是文件夹,递归调用自身方法,如果是文件,则可以直接操作。 二、Java后台文件夹遍历完整代码 import …

    Java 2023年5月20日
    00
  • SpringBoot分离打Jar包的两种配置方式

    Spring Boot 是一种快速创建独立的、基于Spring的应用程序的方式,具有代码少、配置简单、开发效率高、开箱即用等特点。在实际应用中,我们通常需要将 Spring Boot 应用程序打包为一个可执行的 jar 包,以方便进行部署和运行。而分离打 jar 包则是将引用的依赖库全部打包进来的方式,使得打包后的 jar 包可以直接运行,不需要依赖外部的类…

    Java 2023年5月19日
    00
合作推广
合作推广
分享本页
返回顶部