Spring Boot API认证咨询:角色权限控制与客户端对接问题
解决方案
一、用Spring Security实现Token认证与角色权限控制
1. 添加依赖
在pom.xml中引入核心依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- JWT相关依赖 --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency>
2. 配置Security规则
创建SecurityConfig类,关闭默认表单登录,配置URL权限拦截,添加JWT过滤器:
@Configuration @EnableWebSecurity public class SecurityConfig { @Autowired private JwtAuthenticationFilter jwtAuthFilter; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(auth -> auth .requestMatchers("/api/public/**").permitAll() // 公共接口允许匿名访问 .requestMatchers("/api/admin/**").hasRole("ADMIN") // admin接口需ADMIN角色 .requestMatchers("/api/user/**").hasAnyRole("USER", "ADMIN") // user接口允许USER/ADMIN .anyRequest().authenticated() ) .exceptionHandling(ex -> ex .authenticationEntryPoint((request, response, authException) -> { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "未授权:请携带有效Token"); }) .accessDeniedHandler((request, response, accessDeniedException) -> { response.sendError(HttpServletResponse.SC_FORBIDDEN, "权限不足:无访问该接口的权限"); }) ) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } // 配置用户认证管理器(示例:从数据库取用户,这里简化) @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { return config.getAuthenticationManager(); } }
3. 实现JWT过滤器
编写JwtAuthenticationFilter负责解析请求头中的Token,验证并注入用户信息:
@Component public class JwtAuthenticationFilter extends OncePerRequestFilter { @Value("${jwt.secret}") private String jwtSecret; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String authHeader = request.getHeader("Authorization"); String token = null; String username = null; // 提取Bearer Token if (authHeader != null && authHeader.startsWith("Bearer ")) { token = authHeader.substring(7); try { username = Jwts.parserBuilder() .setSigningKey(Keys.hmacShaKeyFor(jwtSecret.getBytes())) .build() .parseClaimsJws(token) .getBody() .getSubject(); } catch (JwtException e) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token无效或已过期"); return; } } // 验证用户并注入SecurityContext if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { // 这里可以从数据库查询用户角色,示例中硬编码角色 List<GrantedAuthority> authorities = Arrays.asList( new SimpleGrantedAuthority("ROLE_USER"), new SimpleGrantedAuthority("ROLE_ADMIN") // 根据实际用户角色设置 ); Authentication auth = new UsernamePasswordAuthenticationToken(username, null, authorities); SecurityContextHolder.getContext().setAuthentication(auth); } filterChain.doFilter(request, response); } }
4. 登录接口生成Token
创建登录接口,验证用户身份后返回JWT:
@RestController @RequestMapping("/api/public") public class AuthController { @Value("${jwt.secret}") private String jwtSecret; @Value("${jwt.expirationMs}") private long jwtExpirationMs; @PostMapping("/login") public ResponseEntity<String> login(@RequestBody LoginRequest request) { // 这里替换为实际的用户名密码验证逻辑 if ("admin".equals(request.getUsername()) && "admin123".equals(request.getPassword())) { String token = Jwts.builder() .setSubject(request.getUsername()) .setIssuedAt(new Date()) .setExpiration(new Date(new Date().getTime() + jwtExpirationMs)) .claim("roles", Arrays.asList("ROLE_ADMIN")) .signWith(Keys.hmacShaKeyFor(jwtSecret.getBytes()), SignatureAlgorithm.HS256) .compact(); return ResponseEntity.ok(token); } return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("用户名或密码错误"); } } // 登录请求DTO class LoginRequest { private String username; private String password; // getter/setter }
二、关于Auth0的疑问解答
- Auth0支持多个API:在Auth0控制台的「APIs」菜单下可以创建多个API资源,每个API有独立的Audience标识,无需担心只能单API的问题。
- 本地运行时的Audience设置:可以填你本地API的根路径(如
http://localhost:8080/api),或者在Auth0创建API时自定义的Identifier值,两者保持一致即可。 - Spring Boot集成Auth0:引入Auth0的
spring-security-jwt依赖,配置auth0.domain和auth0.audience,然后用JwtWebSecurityConfigurer快速集成token验证,角色权限可以通过Auth0用户的「Roles」配置,token会自动包含roles声明,在Spring Security中用hasRole()即可校验。
三、测试网页客户端的认证方式
1. 基于自定义JWT的客户端实现
- 做一个简单的登录页面,输入用户名密码后调用
/api/public/login接口获取Token,将Token存储在localStorage中。 - 后续调用API时,在请求头中添加
Authorization: Bearer <token>,示例代码(原生JS):
// 登录获取Token async function login() { const response = await fetch('/api/public/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'admin', password: 'admin123' }) }); const token = await response.text(); localStorage.setItem('authToken', token); } // 调用需认证的API async function callProtectedApi() { const token = localStorage.getItem('authToken'); const response = await fetch('/api/admin/dashboard', { headers: { 'Authorization': `Bearer ${token}` } }); const data = await response.json(); console.log(data); }
2. 基于Auth0的客户端实现
- 使用Auth0提供的Lock组件或自定义登录UI,引导用户登录后获取Token,同样将Token存储在本地,后续请求添加Authorization头。
- Auth0会自动处理Token的刷新、过期等逻辑,适合快速搭建测试客户端。
内容的提问来源于stack exchange,提问作者yolo_do
相关产品推荐
相关产品推荐

