java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringSecurity权限认证

SpringSecurity实现权限认证的完整示例代码

作者:阿狗童鞋

Spring Security是Java后端安全认证与授权的事实标准,也是 CSDN Java 板块的高频话题,前面讲过Spring核心IoC/AOP,这篇专门讲安全:认证流程、过滤器链、JWT 集成、方法级权限、密码加密、OAuth2、常见坑,全程带SpringBoot代码,需要的朋友可以参考下

一、Spring Security 是什么

1.1 定位

Spring Security 提供**认证(你是谁)和授权(你能干什么)**两大能力:

1.2 为什么用它

二、快速开始

2.1 依赖

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

加入依赖后默认行为:所有接口都要登录,默认用户 user,密码在启动日志里(随机生成)。

2.2 基础配置

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())                  // 前后端分离关 CSRF
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/**").permitAll()   // 登录注册放行
                .requestMatchers("/api/admin/**").hasRole("ADMIN")
                .requestMatchers("/api/user/**").hasRole("USER")
                .anyRequest().authenticated()              // 其余都要登录
            )
            .formLogin(form -> form.disable())             // 禁用表单登录
            .httpBasic(basic -> basic.disable());          // 禁用 Basic
        return http.build();
    }
}

2.3 内存用户(测试用)

@Bean
public UserDetailsService userDetailsService() {
    UserDetails admin = User.withUsername("admin")
        .password(passwordEncoder().encode("123456"))
        .roles("ADMIN")
        .build();

    UserDetails user = User.withUsername("user")
        .password(passwordEncoder().encode("123456"))
        .roles("USER")
        .build();

    return new InMemoryUserDetailsManager(admin, user);
}

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

三、认证流程(过滤器链)

3.1 核心过滤器链

请求
  → SecurityContextPersistenceFilter(恢复上下文)
  → UsernamePasswordAuthenticationFilter(表单登录)
  → BasicAuthenticationFilter(Basic 认证)
  → BearerTokenAuthenticationFilter(JWT/OAuth2 Token)
  → AnonymousAuthenticationFilter(匿名)
  → ExceptionTranslationFilter(异常转 401/403)
  → AuthorizationFilter(授权判断)
  → 业务接口

记住:请求先过认证过滤器,再过授权过滤器。

3.2 认证核心组件

组件作用
Authentication认证信息(未认证/已认证)
AuthenticationManager认证入口
AuthenticationProvider具体认证逻辑(查库、校验密码)
UserDetailsService按用户名查用户
SecurityContext当前登录上下文
SecurityContextHolder保存上下文的容器

3.3 从数据库认证(生产标准)

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
        User user = userMapper.selectByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("用户不存在");
        }
        return org.springframework.security.core.userdetails.User
            .withUsername(user.getUsername())
            .password(user.getPassword())          // 存的是 BCrypt 密文
            .roles(user.getRole())                 // "ADMIN" / "USER"
            .build();
    }
}

四、JWT 集成(前后端分离标准方案)

4.1 为什么用 JWT

4.2 JWT 结构

Header(算法).Payload(claims).Signature(签名)

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiJ9.签名

4.3 登录接口生成 Token

@Service
public class AuthService {

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private JwtUtil jwtUtil;

    public String login(LoginDTO dto) {
        // 1. 认证(校验用户名密码)
        Authentication auth = authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(
                dto.getUsername(), dto.getPassword()));

        // 2. 认证成功 → 生成 token
        UserDetails user = (UserDetails) auth.getPrincipal();
        return jwtUtil.generateToken(user.getUsername());
    }
}
// JwtUtil(简化版,生产用 jjwt 库)
@Component
public class JwtUtil {

    private static final SecretKey KEY =
        Keys.hmacShaKeyFor("your-secret-key-please-change-it-32bytes".getBytes());

    public String generateToken(String username) {
        return Jwts.builder()
            .setSubject(username)
            .claim("role", "ADMIN")
            .setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + 3600_000))  // 1h
            .signWith(KEY)
            .compact();
    }

    public String parseUsername(String token) {
        return Jwts.parserBuilder()
            .setSigningKey(KEY)
            .build()
            .parseClaimsJws(token)
            .getBody()
            .getSubject();
    }
}

4.4 JWT 过滤器

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUtil jwtUtil;
    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
            HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        String header = request.getHeader("Authorization");
        if (header != null && header.startsWith("Bearer ")) {
            String token = header.substring(7);
            try {
                String username = jwtUtil.parseUsername(token);
                if (username != null
                        && SecurityContextHolder.getContext().getAuthentication() == null) {
                    UserDetails user = userDetailsService.loadUserByUsername(username);
                    var auth = new UsernamePasswordAuthenticationToken(
                        user, null, user.getAuthorities());
                    SecurityContextHolder.getContext().setAuthentication(auth);
                }
            } catch (Exception e) {
                // token 无效:不设置认证,走匿名
            }
        }
        chain.doFilter(request, response);
    }
}

4.5 注册过滤器

// 在 SecurityConfig 里加
http.addFilterBefore(jwtAuthenticationFilter,
    UsernamePasswordAuthenticationFilter.class);

五、方法级权限

5.1 开启

@Configuration
@EnableWebSecurity
@EnableMethodSecurity        // 开启方法级权限
public class SecurityConfig { }

5.2 注解使用

@RestController
public class OrderController {

    @GetMapping("/orders")
    @PreAuthorize("hasRole('USER')")          // 需要 USER 角色
    public List<Order> myOrders() { ... }

    @DeleteMapping("/orders/{id}")
    @PreAuthorize("hasRole('ADMIN')")         // 需要 ADMIN 角色
    public void deleteOrder(@PathVariable Long id) { ... }

    @GetMapping("/profile")
    @PreAuthorize("hasAuthority('user:read')")  // 按权限点
    public Profile profile() { ... }
}

5.3 常用表达式

表达式含义
hasRole(‘ADMIN’)有 ADMIN 角色
hasAnyRole(‘ADMIN’,‘USER’)任一角色
hasAuthority(‘user:read’)有某权限点
#id == authentication.principal.idSpEL 比对参数
isAuthenticated()已登录
permitAll() / denyAll()全放行/全拒绝

5.4 拿到当前用户

@GetMapping("/me")
public User me() {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    return userService.findByUsername(auth.getName());
}

// 或参数注入
@GetMapping("/me2")
public User me2(@AuthenticationPrincipal UserDetails user) {
    return userService.findByUsername(user.getUsername());
}

六、密码安全

6.1 BCrypt(默认推荐)

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();    // 自动加盐,不可逆
}

// 注册时加密存储
user.setPassword(passwordEncoder.encode(rawPassword));

// 登录时校验
boolean ok = passwordEncoder.matches(rawPassword, storedHash);

6.2 为什么不用 MD5/SHA

七、OAuth2 / 第三方登录(可选)

7.1 概念

OAuth2 让用户"授权"第三方应用访问其资源,而不交出密码。

用户 → 授权服务器(微信/GitHub)→ 授权码
     → 应用服务器用授权码 + client_secret 换 token
     → 用 token 调资源接口拿用户信息

7.2 Spring Boot 集成

spring:
  security:
    oauth2:
      client:
        registration:
          github:
            client-id: xxx
            client-secret: xxx
http.oauth2Login(oauth -> oauth
    .defaultSuccessUrl("/", true));

企业内常用:OAuth2 + 微信/钉钉扫码登录(前面有钉钉插件能力)。

八、常见坑速查

现象原因解决
加了依赖全站要登录默认安全策略配置 permitAll
密码错误但明明对密码不是 BCrypt用 encoder.matches
登录后 403角色前缀问题hasRole 自动加 ROLE_
JWT 过滤器不执行没注册addFilterBefore
自定义接口 404放行了但没权限检查 matcher 顺序
改配置不生效缓存重启/清缓存
前后端分离 403CSRF关 csrf 或配白名单
方法权限不生效没开 @EnableMethodSecurity开启注解

本章小结

Spring Security 的心智模型:过滤器链做认证,AuthorizationFilter 做授权,SecurityContext 存当前用户。前后端分离项目的标准组合 = JWT 过滤器 + BCrypt 密码 + 方法级 @PreAuthorize。记住角色有 ROLE_ 前缀、密码必须 BCrypt、Token 必须有过期时间这三个要点,就能避免 90% 的坑。

以上就是SpringSecurity权限认证实战指南的详细内容,更多关于SpringSecurity权限认证的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文