使用 Spring Security 和 JWT 来实现动态菜单的步骤包括用户身份验证、生成和验证 JWT 令牌,以及根据用户的角色或权限动态生成菜单。这种实现方式需要你在后端进行身份认证和授权管理,并在前端根据用户信息动态展示菜单。
实现步骤
- 用户身份验证和 JWT 生成
- JWT 验证和授权
- 动态菜单生成
步骤 1: 用户身份验证和 JWT 生成
首先,用户通过登录接口提交用户名和密码进行身份验证,成功后生成 JWT 令牌返回给用户。
代码示例:
1.1 用户登录控制器
@RestController
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@PostMapping("/login")
public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest loginRequest) {
// 进行用户身份认证
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getUsername(),
loginRequest.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
// 生成JWT令牌
String jwt = jwtTokenUtil.generateToken(authentication);
return ResponseEntity.ok(new JwtResponse(jwt));
}
}
1.2 JWT 工具类
@Component
public class JwtTokenUtil {
private String jwtSecret = "yourSecretKey"; // 你的密钥
public String generateToken(Authentication authentication) {
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
Date now = new Date();
Date expiryDate = new Date(now.getTime() + 86400000); // 设置令牌过期时间
return Jwts.builder()
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
public String getUsernameFromToken(String token) {
return Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(token)
.getBody()
.getSubject();
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}
}
步骤 2: JWT 验证和授权
在每个请求中通过 JWT 进行用户身份验证,验证成功后根据用户信息来确定用户的角色或权限。
代码示例:
2.1 JWT 过滤器
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = getJwtFromRequest(request);
if (StringUtils.hasText(token) && jwtTokenUtil.validateToken(token)) {
String username = jwtTokenUtil.getUsernameFromToken(token);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
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;
}
}
2.2 安全配置类
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = getJwtFromRequest(request);
if (StringUtils.hasText(token) && jwtTokenUtil.validateToken(token)) {
String username = jwtTokenUtil.getUsernameFromToken(token);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
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;
}
}
步骤 3: 动态菜单生成
用户成功登录后,后端根据用户的角色或权限生成相应的菜单数据,前端再根据这个数据渲染菜单。
代码示例:
3.1 获取菜单数据的控制器
@RestController
public class MenuController {
@GetMapping("/menu")
public ResponseEntity<?> getUserMenu(Authentication authentication) {
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
List<String> roles = userDetails.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList());
// 根据用户角色生成菜单
List<MenuItem> menuItems = generateMenuBasedOnRoles(roles);
return ResponseEntity.ok(menuItems);
}
private List<MenuItem> generateMenuBasedOnRoles(List<String> roles) {
// 这里根据角色生成动态菜单
List<MenuItem> menuItems = new ArrayList<>();
if (roles.contains("ROLE_ADMIN")) {
menuItems.add(new MenuItem("Dashboard", "/dashboard"));
menuItems.add(new MenuItem("User Management", "/users"));
} else if (roles.contains("ROLE_USER")) {
menuItems.add(new MenuItem("Profile", "/profile"));
menuItems.add(new MenuItem("Orders", "/orders"));
}
return menuItems;
}
}
3.2 菜单数据模型
public class MenuItem {
private String name;
private String url;
public MenuItem(String name, String url) {
this.name = name;
this.url = url;
}
// Getter和Setter方法
}