spring security 6.3.3 + java-jwt 3.10.3

spring security 6.3.3 + java-jwt 3.10.3 模拟前后端分离使用token验证登录

例子中实现了用户通过security验证成功后获取后端返回的token,在token有效期内可以访问受保护的 /user_request 接口,通过token验证后获取后端返回的数据
项目结构

在这里插入图片描述

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.3</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.security</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>demo</name>
	<description>Demo project for Spring Boot</description>
	<url/>
	<licenses>
		<license/>
	</licenses>
	<developers>
		<developer/>
	</developers>
	<scm>
		<connection/>
		<developerConnection/>
		<tag/>
		<url/>
	</scm>
	<properties>
		<java.version>21</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>3.0.3</version>
		</dependency>
		<dependency>
			<groupId>org.thymeleaf.extras</groupId>
			<artifactId>thymeleaf-extras-springsecurity6</artifactId>
		</dependency>
		<dependency>
			<groupId>com.alibaba.fastjson2</groupId>
			<artifactId>fastjson2</artifactId>
			<version>2.0.51</version>
		</dependency>
		<dependency>
			<groupId>com.mysql</groupId>
			<artifactId>mysql-connector-j</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>com.auth0</groupId>
			<artifactId>java-jwt</artifactId>
			<version>3.10.3</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter-test</artifactId>
			<version>3.0.3</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

	<repositories>
		<repository>
			<id>maven_central</id>
			<name>Maven Central</name>
			<url>https://repo.maven.apache.org/maven2/</url>
		</repository>
	</repositories>
</project>
pojo - DbUser
package com.security.demo.pojo;

import java.io.Serializable;

/**
 * 和数据库里所有字段分别对应的实体类
 */
public class DbUser implements Serializable {

    private int id;
    private String username;
    private String password;
    private String source;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getSource() {
        return source;
    }

    public void setSource(String source) {
        this.source = source;
    }

    @Override
    public String toString() {
        return "DbUser{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", source='" + source + '\'' +
                '}';
    }
}
pojo - LoginUser
package com.security.demo.pojo;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;

/**
 * 实现了UserDetails接口的实体类,以供security用来做后续是否有权限登录的校验
 */
public class LoginUser implements Serializable, UserDetails {

    /**
     * 使用DbUser做为该实体类的字段
     * 因为LoginUser的getUsername()和getPassword()两个方法返回的值
     * 可以从DbUser里获得
     */
    private DbUser dbUser;

    public DbUser getUser() {
        return dbUser;
    }

    public void setUser(DbUser dbUser) {
        this.dbUser = dbUser;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return List.of();
    }

    @Override
    public String getPassword() {
        return dbUser.getPassword();
    }

    @Override
    public String getUsername() {
        return dbUser.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return UserDetails.super.isAccountNonExpired();
    }

    @Override
    public boolean isAccountNonLocked() {
        return UserDetails.super.isAccountNonLocked();
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return UserDetails.super.isCredentialsNonExpired();
    }

    @Override
    public boolean isEnabled() {
        return UserDetails.super.isEnabled();
    }

    @Override
    public String toString() {
        return "LoginUser{" + "DbUser=" + dbUser + "}";
    }
}
pojo - ResultData
package com.security.demo.pojo;

import java.io.Serializable;

/**
 * 封装返回前端的统一格式
 */
public class ResultData implements Serializable {

    private int code;
    private String msg;
    private Object data;

    private ResultData() {}

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }

    public Object getData() {
        return data;
    }

    /**
     * 链式调用设置属性入参
     */
    public static class ResultDataBuild {

        private ResultDataBuild() {}

        ResultData resultData = new ResultData();

        public ResultDataBuild setCode(int code) {
            resultData.code = code;
            return this;
        }

        public ResultDataBuild setMsg(String msg) {
            resultData.msg = msg;
            return this;
        }

        public ResultDataBuild setData(Object object) {
            resultData.data = object;
            return this;
        }

        public ResultData build() {
            return resultData;
        }

    }

    public static ResultDataBuild builder() {
        return new ResultDataBuild();
    }
}
LoginUserAuthorization
package com.security.demo.config.authentication;

import com.security.demo.dao.DemoDao;
import com.security.demo.pojo.LoginUser;
import com.security.demo.pojo.DbUser;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

import java.util.Objects;

/**
 * 实现UserDetailsService接口的自定义UserDetailsService类型的类
 * 在自定义的AuthenticationManager中会使用到该类
 * 重写了loadUserByUsername(),里边获取到从DB里查出来相同username的信息
 * 返回UserDetails类型的LoginUser,后续AuthenticationManager会自动进行是否有权限登录的校验
 */
@Component
public class LoginUserAuthorization implements UserDetailsService {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoginUserAuthorization.class);

    @Resource
    private DemoDao demoDao;

    @Override
    public UserDetails loadUserByUsername(String username) {
        DbUser dbUser = null;
        try {
            dbUser = demoDao.queryByName(username);
        } catch (Exception e) {
            LOGGER.error("在这里准备记录一个可能不会发生的错误");
        }
        if (Objects.isNull(dbUser)) {
            LOGGER.error("库里不存在该用户名字");
            throw new UsernameNotFoundException("用户名或密码错误");
        }
        LoginUser loginUser = new LoginUser();
        loginUser.setUser(dbUser);
        return loginUser;
    }
}
TokenAuthorization
package com.security.demo.config.authentication;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.security.demo.utils.RedisUtil;
import com.security.demo.utils.TokenUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Objects;

/**
 * OncePerRequestFilter是每个请求只经过一次该过滤器
 */
@Component
public class TokenAuthorization extends OncePerRequestFilter {

    @Resource
    private RedisUtil redisUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String token = request.getHeader("token");
        if (!StringUtils.hasText(token)) {
            //放行让后边的过滤器判断
            filterChain.doFilter(request, response);
            return;
        }
        //解析token
        String authorization = TokenUtil.parseToken(token);
        Object value = redisUtil.getKey(authorization);
        if (Objects.isNull(authorization) && Objects.isNull(value)) {
            throw new RuntimeException("token无效");
        }
        //从redis里获取存入的信息
        JSONObject object = JSON.parseObject(value.toString());
        //封装信息放入SecurityContextHolder
        UsernamePasswordAuthenticationToken authenticationToken;
        //credentials和authorities目前没有都设为null
        authenticationToken = new UsernamePasswordAuthenticationToken(object, null, null);
        //验证信息放入SecurityContextHolder上下文
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        //继续放行
        filterChain.doFilter(request, response);
    }
}
RedisConfig
package com.security.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * Redis配置类
 */
@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        //key使用string序列化
        template.setKeySerializer(new StringRedisSerializer());
        //value使用string序列化
        template.setValueSerializer(new StringRedisSerializer());
        //初始化
        template.afterPropertiesSet();
        return template;
    }

}
SecurityConfig
package com.security.demo.config;

import com.security.demo.config.authentication.LoginUserAuthorization;
import com.security.demo.config.authentication.TokenAuthorization;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 *  security的配置类
 */
@Configuration
public class SecurityConfig {

    @Resource
    private LoginUserAuthorization loginUserAuthorization;

    @Resource
    private TokenAuthorization tokenAuthorization;

    /**
     * 定义了password使用的加密方式
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 为AuthenticationManager里定义了UserDetailsService当中方法的执行逻辑和password的加密方式
     * ProviderManager是AuthenticationManager接口的具体实现类
     * 该类里实现了authenticate方法是做具体验证的
     * @return
     */
    @Bean
    public AuthenticationManager authenticationManager() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(loginUserAuthorization);
        provider.setPasswordEncoder(passwordEncoder());
        return new ProviderManager(provider);
    }

    /**
     * security全是由过滤器链的方式实现的
     * 该例中只做了一些最简单的配置
     * @param http
     * @return
     * @throws Exception
     */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        //本地测试关了csrf
        http.csrf(AbstractHttpConfigurer::disable);
                                        //只允许/demo/register接口可以不受任何限制的访问
        http.authorizeHttpRequests(r -> r.requestMatchers("/demo/user_login", "/demo/register").permitAll()
                                        //对于其它任何接口的访问都要经过身份认证方可
                                         .anyRequest().authenticated());

        //使用security美观简洁的默认登录页,使用postman模拟前后端分离登录则不需要该登录页
        //http.formLogin(Customizer.withDefaults());

        //将自定义token验证过滤器放在UsernamePasswordAuthenticationFilter之前
        http.addFilterBefore(tokenAuthorization, UsernamePasswordAuthenticationFilter.class);
        //返回配置好的过滤器链逻辑
        return http.build();
    }
}
TokenUtil
package com.security.demo.utils;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Date;

/**
 * 生成token和解析token的工具类
 */
public class TokenUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(TokenUtil.class);

    //应该算是不常见的密钥
    public static final String SECRET = "ABCDEFG_HIGKLMN_12345_67890";

    //默认过期时间1个小时=3600000毫秒
    public static final long EXPIRATION = 3600000;

    //使用密钥生成该算法的实例
    private static final Algorithm ALGORITHM = Algorithm.HMAC256(SECRET);

    /**
     * 生成Token
     * @param id
     * @return
     */
    public static String generateToken(String id) {
        String token = null;
        try {
            token = JWT.create()
                    //设置过期时间
                    .withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION))
                    //设置payload, 里边准备存放的是UUID
                    .withClaim("Authorization", id)
                    //设置签名
                    .sign(ALGORITHM);
        } catch (Exception e) {
            LOGGER.error("生成JWT出现异常");
            return token;
        }
        return token;
    }

    /**
     * 解析Token
     * @param token
     * @return
     */
    public static String parseToken(String token) {
        String authorization = null;
        try {
            //返回verifier实例
            JWTVerifier verifier = JWT.require(ALGORITHM).build();
            //使用verifier实例验证Token并获取Base64格式的payload内容
            String payload = verifier.verify(token).getPayload();
            //使用Base64解码
            byte[] decode = Base64.getDecoder().decode(payload);
            //将解码后的字节使用UTF-8转为可读String
            String string = new String(decode, StandardCharsets.UTF_8);
            //将String内容转为JsonObject并获取需要的数据
            JSONObject jsonObject = JSON.parseObject(string);
            authorization = jsonObject.get("Authorization").toString();
        } catch (JWTVerificationException e) {
            LOGGER.error("验证token出现异常");
            return authorization;
        }
        return authorization;
    }
}
RedisUtil
package com.security.demo.utils;

import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Objects;

/**
 * Redis存取值工具类
 */
@Component
public class RedisUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(RedisUtil.class);

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    public boolean setKeyValue(String key, Object value) {
        boolean result = false;
        try {
            redisTemplate.opsForValue().set(key, value);
            result = true;
        } catch (Exception e) {
            LOGGER.error("Redis在存放时出现异常");
        }
        return result;
    }

    public Object getKey(String key) {
        Object object = null;
        if (Objects.isNull(key) || key.isBlank()) {
            return object;
        }
        try {
            object = redisTemplate.opsForValue().get(key);
        } catch (Exception e) {
            LOGGER.error("Redis取值出现异常");
        }
        return object;
    }

    public boolean deleteKey(String key) {
        boolean result = false;
        try {
            result = redisTemplate.delete(key);
        } catch (Exception e) {
            LOGGER.error("Redis册除值出现异常");
        }
        return result;
    }
}
DemoController
package com.security.demo.controller;

import com.alibaba.fastjson2.JSON;
import com.security.demo.pojo.DbUser;
import com.security.demo.pojo.ResultData;
import com.security.demo.service.DemoService;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/demo")
public class DemoController {

    private static final Logger LOGGER = LoggerFactory.getLogger(DemoController.class);

    @Resource
    private DemoService demoService;

    /**
     * 获取所有已存在的用户
     * @return
     */
    @RequestMapping("/list")
    public String list() {
        List<DbUser> dbUsers = demoService.queryAll();
        return JSON.toJSONString(dbUsers);
    }

    /**
     * 注册一个新用户
     * @param dbUser
     * @return
     */
    @RequestMapping("/register")
    public String register(@RequestBody DbUser dbUser) {
        String username = dbUser.getUsername();
        int row = demoService.register(dbUser);
        if (row == 0) {
            username = username.concat(": 注册失败,用户名已存在");
            LOGGER.error("{}:", username);
        }
        if (row == 1) {
            username = username.concat(": 注册成功");
            LOGGER.info("{}:", username);
        }
        return username;
    }

    /**
     * 模拟用户登录通过验证并返回token
     * @param dbUser
     * @return
     */
    @RequestMapping("/user_login")
    public ResultData login(@RequestBody DbUser dbUser) {
       return demoService.login(dbUser);
    }

    /**
     * 携带着登录后返回的token在访问受保护的接口时解析token成功后返回数据
     * @param token
     * @return
     */
    @RequestMapping("/user_request")
    public ResultData requestData(@RequestHeader("token") String token) {
        return demoService.requestData(token);
    }
}
DemoService
package com.security.demo.service;

import com.security.demo.pojo.DbUser;
import com.security.demo.pojo.ResultData;

import java.util.List;

public interface DemoService {

    List<DbUser> queryAll();

    int register(DbUser dbUser);

    ResultData login(DbUser dbUser);

    ResultData requestData(String token);
}
DemoServiceImpl
package com.security.demo.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.security.demo.dao.DemoDao;
import com.security.demo.pojo.DbUser;
import com.security.demo.pojo.ResultData;
import com.security.demo.service.DemoService;
import com.security.demo.utils.RedisUtil;
import com.security.demo.utils.TokenUtil;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

@Service
public class DemoServiceImpl implements DemoService {

    private static final Logger LOGGER = LoggerFactory.getLogger(DemoServiceImpl.class);

    @Resource
    private DemoDao demoDao;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private RedisUtil redisUtil;

    /**
     * 获取所有用户信息
     * @return
     */
    @Override
    public List<DbUser> queryAll() {
        return demoDao.queryAll();
    }

    /**
     * 注册一个新用户
     * @param dbUser
     * @return
     */
    @Override
    public int register(DbUser dbUser) {
        int row = 0;
        if (checkUserNameIsExist(dbUser.getUsername())) {
            return row;
        }
        try {
            String password = dbUser.getPassword();
            String encode = passwordEncoder.encode(password);
            dbUser.setPassword(encode);
            row = demoDao.register(dbUser);
        } catch (Exception e) {
            LOGGER.error("用户在注册时出现异常");
        }
        return row;
    }

    @Override
    public ResultData login(DbUser dbUser) {
        try {
            UsernamePasswordAuthenticationToken authenticationToken =
                    new UsernamePasswordAuthenticationToken(dbUser.getUsername(), dbUser.getPassword());
            authenticationManager.authenticate(authenticationToken);
        } catch (AuthenticationException e) {
            return ResultData.builder().setCode(403).setMsg("用户名或密码错误").setData(null).build();
        }
        String uuid = UUID.randomUUID().toString();
        String token = TokenUtil.generateToken(uuid);
        redisUtil.setKeyValue(uuid, JSON.toJSONString(dbUser));
        return ResultData.builder().setCode(200).setMsg("登录成功并获取token").setData(Map.of("token", token)).build();
    }

    @Override
    public ResultData requestData(String token) {
        String authorization = TokenUtil.parseToken(token);
        Object object = redisUtil.getKey(authorization);
        JSONObject jo = JSON.parseObject(object.toString());
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(jo.get("username"), jo.get("password"));
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        if (Objects.isNull(authenticate)) {
            throw new RuntimeException("用户信息错误");
        }
        List<DbUser> dbUsers = demoDao.queryAll();
        return ResultData.builder().setCode(200).setMsg("查到所有信息").setData(Map.of("userAll", dbUsers)).build();
    }

    /**
     * 在注册的时候检查该用户名字是否已存在
     * @param username
     * @return
     */
    private boolean checkUserNameIsExist(String username) {
        boolean result = false;
        try {
            DbUser dbUser = demoDao.checkUserByName(username);
            if (!Objects.isNull(dbUser)) {
                result = true;
                LOGGER.error("新用户名字已存在");
            }
        } catch (Exception e) {
            LOGGER.error("在这里准备记录一个可能不会发生的错误");
        }
        return result;
    }
}
DemoDao
package com.security.demo.dao;

import com.security.demo.pojo.DbUser;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface DemoDao {

    //获取所有用户信息
    List<DbUser> queryAll();

    //根据username获取对应的用户信息
    DbUser queryByName(String username);

    //注册一个新用户
    int register(DbUser dbUser);

    //检查username是否已存在
    DbUser checkUserByName(String username);
}
Spring Boot项目中整合Spring Security 6.3.3,首先需要添加Spring Security依赖到你的`build.gradle`或`pom.xml`文件中。如果你使用的是Gradle: ```groovy dependencies { implementation &#39;org.springframework.boot:spring-boot-starter-security&#39; } ``` 对于Maven用户,添加类似下面的依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> ``` 接下来,配置Spring Security的基本设置。在`application.properties`或`application.yml`中可以添加一些基本的配置,比如: ```yaml # application.yml spring.security: user: name: your_username password: your_password management: security: enabled: false # 如果你不想管理端也受保护,可以关闭这行 ``` 创建一个`UserDetailsService`实现,负责从数据库或其他数据源加载用户信息: ```java @Service public class MyUserService implements UserDetailsService { @Autowired private UserRepository userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 根据username查询数据库获取User对象,然后构建UserDetails对象返回 } } ``` 配置WebSecurityConfigurerAdapter或更现代的SecurityConfig,处理HTTP会话管理和授权规则: ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/login", "/register").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .defaultSuccessUrl("/") .permitAll() .and() .logout() .logoutSuccessUrl("/") .permitAll(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService); } } ``` 完成上述步骤后,你可以开始编写访问控制逻辑,例如基于角色的权限管理(@PreAuthorize注解),并在需要的地方使用Spring Security提供的安全拦截器。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值