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日

相关文章

  • SpringMVC4 + MyBatis3 + SQL Server 2014整合教程(含增删改查分页)

    下面是关于“SpringMVC4 + MyBatis3 + SQL Server 2014整合教程(含增删改查分页)”的完整攻略,包含两个示例说明。 SpringMVC4 + MyBatis3 + SQL Server 2014整合教程 在本文中,我们将介绍如何使用SpringMVC4、MyBatis3和SQL Server 2014实现一个简单的增删改查分…

    Java 2023年5月17日
    00
  • java(包括springboot)读取resources下文件方式实现

    下面是详细讲解“java(包括springboot)读取resources下文件方式实现”的完整攻略。 1. 背景 在Java中,经常需要读取resources下的文件。resources文件夹通常位于项目的classpath下,可以存放各种类型的文件,如文本文件、配置文件、图片等。这里将对读取resource文件夹下文件的几种常用方法进行讲解。 2. 使用…

    Java 2023年5月19日
    00
  • java操作Apache druid的实例代码

    下面是一份针对Java操作Apache Druid的实例代码的完整攻略。 1. 安装Apache Druid 首先需要在本地或云主机上安装Apache Druid,并且按照官方文档进行配置和启动。 2. 引入依赖 在Java项目中,需要引入Druid提供的Java客户端库依赖: <dependency> <groupId>org.ap…

    Java 2023年5月20日
    00
  • Spring Boot教程之必须了解的核心概念

    Spring Boot教程之必须了解的核心概念 Spring Boot是一个基于Spring框架的快速开发框架,许多开发人员都选择使用它来进行项目开发。本篇教程将介绍Spring Boot的一些核心概念。 1. 自动配置 Spring Boot使用自动配置的方式,可以大大减轻我们的负担。它会根据classpath中的jar包,自动配置应用程序所需的依赖项。如…

    Java 2023年5月19日
    00
  • Java中字符串常见的一些拼接方式总结

    Java 中字符串的拼接是一个较为常见的操作,也是 Java 语言重要组成部分。本篇攻略将为大家详细讲解 Java 中字符串常见的拼接方式以及相应的示例说明。 字符串拼接方式总结 在 Java 中,字符串的拼接方式有以下几种: 1. 使用 “+” 号拼接 String str1 = "Hello,"; String str2 = &quo…

    Java 2023年5月26日
    00
  • Sprint Boot @Import使用方法详解

    在Spring Boot中,@Import注解是一种用于导入其他配置类或组件的注解。使用@Import注解可以将其他配置类或组件导入到当前配置类中,从而实现组件的复用和模块化。本文将详细介绍@Import注解的作用和使用方法,并提供两个示例说明。 @Import注解的作用 在Spring Boot中,@Import注解的作用是将其他配置类或组件导入到当前配置…

    Java 2023年5月5日
    00
  • Java SpringBoot @Async实现异步任务的流程分析

    针对你提出的这个问题,我将会按照以下步骤来给出完整的攻略: 介绍什么是SpringBoot @Async 讲解SpringBoot @Async的工作流程 提供两个示例,展示如何使用SpringBoot @Async来实现异步任务 1. 什么是SpringBoot @Async SpringBoot @Async是一个实现异步任务的开发框架。通过使用@Asy…

    Java 2023年5月20日
    00
  • java批量修改文件后缀名方法总结

    Java批量修改文件后缀名方法总结 在Java中,我们可以使用File类实现批量修改文件后缀名的操作。在本文中,我们将介绍使用Java修改文件后缀名的完整攻略以及两条示例。 1. Java批量修改文件后缀名的方法 1.1 获取文件夹下所有文件 使用File类的listFiles()方法可以获取某个文件夹下的所有文件,该方法返回一个File数组,数组中包含了该…

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