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

yizhihongxing

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

  • 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日

相关文章

  • J2SE中的序列化之继承

    J2SE中的序列化是将对象转换成字节流,用于对象的存储和传输。而在序列化对象时,如果该对象实现了Serializable接口,那么子类也会自动实现序列化,这就是所谓的“继承序列化”。 下面通过示例说明继承序列化的几个要点: 1.子类序列化时父类属性的序列化与反序列化: public class Parent implements Serializable{ …

    Java 2023年6月15日
    00
  • Java面向对象编程之类的继承详解

    Java面向对象编程之类的继承详解 什么是继承? 继承是一种面向对象编程的重要特性,它可以让一个类(子类)拥有另一个类(父类)的所有方法和属性。 在Java中,使用关键字extends来实现继承。 继承的语法 public class ChildClass extends ParentClass { // 子类的内容 } 在以上语法中,我们定义了一个名为Ch…

    Java 2023年5月26日
    00
  • java IO流读取图片供前台显示代码分享

    下面是Java IO流读取图片供前台显示的完整攻略: 一、概述 在Java中,使用IO流读取图片供前台显示可以分为以下几个步骤: 使用Java IO流读取图片文件到内存中; 将读取到的图片字节流转换为Base64编码; 将Base64编码的图片数据返回给前台。 二、代码示例 以下是两条示例代码,可以供您参考: 使用FileInputStream和ByteAr…

    Java 2023年5月19日
    00
  • Java中String和StringBuffer及StringBuilder 有什么区别

    Java中String、StringBuffer和StringBuilder都是关于字符串的类,但它们有着不同的特点和用法。 String类 String类是Java中的一个不可变类,一旦声明并赋值,它的实际内容就无法再被改变了。这是由于它的内部实现是通过一个指向char数组的final引用来实现的。换句话说,一旦String对象被创建,这个引用就不能指向另…

    Java 2023年5月27日
    00
  • 解决IDEA中Maven项目中JSTL标签无效问题

    针对“解决IDEA中Maven项目中JSTL标签无效问题”的完整攻略,以下是具体的步骤: 1. 项目添加JSTL依赖库 首先,在IDEA的Maven项目中需要添加JSTL依赖库,可以在pom.xml中添加以下代码: <dependency> <groupId>javax.servlet</groupId> <arti…

    Java 2023年5月19日
    00
  • 十一、JSP及语法概要

    十一、JSP及语法概要 JSP(Java Server Pages)是Java技术的一种,它允许在程序代码和HTML之间嵌入逻辑代码。使用JSP可以让开发者在不同模块之间进行更好的工作分配,提高项目开发进度和可维护性。 JSP基础 在JSP中可编写JavaScript脚本、HTML代码和Java代码。使用的标签有两种,即Java标签和转译标签。Java标签是…

    Java 2023年6月15日
    00
  • 一小时迅速入门Mybatis之Prepared Statement与符号的使用

    一小时迅速入门Mybatis之Prepared Statement与符号的使用 什么是Mybatis Mybatis是一款优秀的ORM框架,通过XML或注解的方式将Java对象与数据库进行映射,极大地简化了数据库操作的流程。本篇攻略旨在介绍如何快速使用Mybatis的Prepared Statement与符号。 Prepared Statement与符号的使…

    Java 2023年5月20日
    00
  • 使用java采集京东商城行政区划数据示例

    下面是使用Java采集京东商城行政区划数据的完整攻略: 1. 准备 首先需要准备一些工具和资源,包括: JDK 1.8及以上版本 Maven IntelliJ IDEA或Eclipse Jsoup 其中,JDK是Java开发必备的工具,版本需要在1.8及以上,Maven可以管理项目中的依赖,IntelliJ IDEA/Eclipse是Java开发中常用的ID…

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