RBAC 实战
2025-03-23
2025-03-23
978
5 min.
∞
RBAC(Role-Based Access Control)是企业应用中常见的权限控制方式,本文将使用 Java + Spring Security + MySQL 实现一个简易的 RBAC 模块。
数据库表结构设计(DDL) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 CREATE TABLE users ( id BIGINT AUTO_INCREMENT PRIMARY KEY , username VARCHAR (50 ) NOT NULL UNIQUE , password VARCHAR (255 ) NOT NULL ); CREATE TABLE roles ( id BIGINT AUTO_INCREMENT PRIMARY KEY , role_name VARCHAR (50 ) NOT NULL UNIQUE ); CREATE TABLE permissions ( id BIGINT AUTO_INCREMENT PRIMARY KEY , permission_name VARCHAR (50 ) NOT NULL UNIQUE ); CREATE TABLE user_roles ( user_id BIGINT NOT NULL , role_id BIGINT NOT NULL , FOREIGN KEY (user_id) REFERENCES users (id ), FOREIGN KEY (role_id) REFERENCES roles (id ), PRIMARY KEY (user_id, role_id) ); CREATE TABLE role_permissions ( role_id BIGINT NOT NULL , permission_id BIGINT NOT NULL , FOREIGN KEY (role_id) REFERENCES roles (id ), FOREIGN KEY (permission_id) REFERENCES permissions(id ), PRIMARY KEY (role_id, permission_id) );
核心代码 JwtTokenProvider 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 @Service public class JwtTokenProvider { private final String secretKey = "yourSecretKey" ; private final long tokenValidityInMilliseconds = 3600000 ; public String generateToken (Authentication authentication) { UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal(); Date now = new Date(); Date expiryDate = new Date(now.getTime() + tokenValidityInMilliseconds); return Jwts.builder() .setSubject(userPrincipal.getUsername()) .setIssuedAt(now) .setExpiration(expiryDate) .signWith(SignatureAlgorithm.HS512, secretKey) .compact(); } public String getUsernameFromToken (String token) { return Jwts.parser() .setSigningKey(secretKey) .parseClaimsJws(token) .getBody() .getSubject(); } public boolean validateToken (String token) { try { Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token); return true ; } catch (JwtException | IllegalArgumentException e) { return false ; } } }
CustomUserDetailsService 1 2 3 4 5 6 7 8 9 10 11 12 13 @Service public class CustomUserDetailsService implements UserDetailsService { @Autowired private UserRepository userRepository; @Override public UserDetails loadUserByUsername (String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username) .orElseThrow(() -> new UsernameNotFoundException("User not found with username:" + username)); return new UserPrincipal(user); } }
UserPrincipal 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 public class UserPrincipal implements UserDetails { private Long id; private String username; private String password; private Collection<? extends GrantedAuthority> authorities; public UserPrincipal (User user) { this .id = user.getId(); this .username = user.getUsername(); this .password = user.getPassword(); this .authorities = getAuthorities(user); } private Collection<? extends GrantedAuthority> getAuthorities(User user) { return user.getRoles().stream() .flatMap(role -> role.getPermissions().stream()) .map(permission -> new SimpleGrantedAuthority(permission.getPermissionName())) .collect(Collectors.toList()); } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } @Override public String getPassword () { return password; } @Override public String getUsername () { return username; } @Override public boolean isAccountNonExpired () { return true ; } @Override public boolean isAccountNonLocked () { return true ; } @Override public boolean isCredentialsNonExpired () { return true ; } @Override public boolean isEnabled () { return true ; } }
LoginController 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @RestController @RequestMapping ("/api/auth" )public class LoginController { @Autowired private AuthenticationManager authenticationManager; @Autowired private JwtTokenProvider jwtTokenProvider; @PostMapping ("/login" ) public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest loginRequest) { Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken( loginRequest.getUsername(), loginRequest.getPassword() ) ); SecurityContextHolder.getContext().setAuthentication(authentication); String jwt = jwtTokenProvider.generateToken(authentication); Map<String, Object> response = new HashMap<>(); response.put("token" , jwt); return ResponseEntity.ok(response); } }
LoginRequest 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class LoginRequest { private String username; private String password; 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; } }
SecurityConfig 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Autowired private JwtTokenProvider jwtTokenProvider; @Bean public PasswordEncoder passwordEncoder () { return new BCryptPasswordEncoder(); } @Override protected void configure (AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } @Override protected void configure (HttpSecurity http) throws Exception { http .csrf().disable() .authorizeRequests() .antMatchers("/api/auth/**" ).permitAll() .anyRequest().authenticated() .and() .apply(new JwtTokenFilterConfigurer(jwtTokenProvider)); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 public class JwtTokenFilterConfigurer extends SecurityConfigurerAdapter <DefaultSecurityFilterChain , HttpSecurity > { private final JwtTokenProvider jwtTokenProvider; public JwtTokenFilterConfigurer (JwtTokenProvider jwtTokenProvider) { this .jwtTokenProvider = jwtTokenProvider; } @Override public void configure (HttpSecurity http) { JwtTokenFilter customFilter = new JwtTokenFilter(jwtTokenProvider); http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class ) ; } }
JwtTokenFilter 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class JwtTokenFilter extends OncePerRequestFilter { private final JwtTokenProvider jwtTokenProvider; public JwtTokenFilter (JwtTokenProvider jwtTokenProvider) { this .jwtTokenProvider = jwtTokenProvider; } @Override protected void doFilterInternal (HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String token = jwtTokenProvider.resolveToken(request); try { if (token != null && jwtTokenProvider.validateToken(token)) { Authentication authentication = jwtTokenProvider.getAuthentication(token); SecurityContextHolder.getContext().setAuthentication(authentication); } } catch (JwtException e) { SecurityContextHolder.clearContext(); response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid token" ); return ; } filterChain.doFilter(request, response); } }
CustomPermissionEvaluator 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Component public class CustomPermissionEvaluator implements PermissionEvaluator { @Override public boolean hasPermission (Authentication authentication, Object targetDomainObject, Object permission) { if (authentication == null || !(authentication.getPrincipal() instanceof UserPrincipal)) { return false ; } UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal(); return userPrincipal.getAuthorities().contains(new SimpleGrantedAuthority(permission.toString())); } @Override public boolean hasPermission (Authentication authentication, Serializable targetId, String targetType, Object permission) { return false ; } }
应用权限验证 1 2 3 4 5 6 7 8 9 @RestController @RequestMapping ("/api/admin" )public class AdminController { @PreAuthorize ("hasAuthority('ADMIN')" ) @GetMapping ("/dashboard" ) public ResponseEntity<String> getDashboard () { return ResponseEntity.ok("Welcome to the admin dashboard!" ); } }
The post above ended Thanks for your reading
Come on! Write some comments, and your suggestions will improve the quality of my creative!
FRIEND ME
QQ
WeChat
Post Author: WangDeheng
Copyright Notice: All articles/posts in this website are licensed under
BY-NC-SA
unless stating additionally.