WangDeheng
"Eating Sleeping Coding"
9 5 16
SKIN
SETTINGS
简体中文
Night Shift
*1/2 Figure
Hide Lyric
Autoplay
COPY HIGHLIGHT 'N TRANSLATE 中文 | English | 日本語
HITOKOTO
感谢一言网(Hitokoto.cn)提供一句话服务
Wang Deheng
RECENT POSTS
MORE POSTS
POST ARCHIVE
have posts written this year
POST
RBAC 实战

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;

// 生成 JWT Token
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();
}

// 从 Token 中获取用户名
public String getUsernameFromToken(String token) {
return Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token)
.getBody()
.getSubject();
}

// 验证 Token 是否有效
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);
// 生成 JWT Token
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() // 禁用 CSRF 保护
.authorizeRequests() // 配置请求授权规则
.antMatchers("/api/auth/**").permitAll() // 允许所有用户访问登录接口
.anyRequest().authenticated() // 其他所有请求都需要认证
.and()
.apply(new JwtTokenFilterConfigurer(jwtTokenProvider)); // 应用 JWT Token 过滤器
}
}

JwtTokenFilterConfigurer

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); // 从请求中解析 Token
try {
if (token != null && jwtTokenProvider.validateToken(token)) { // 验证 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
Post Link: https://xiaowangblog.cn/posts/18539/
Copyright Notice: All articles/posts in this website are licensed under BY-NC-SA unless stating additionally.
测试框架 Spock
PAGE
UPDATE HISTORY
MORE LESS
CODE STACK
    SAVE_N/A
    N/A
    Type: N/A
    Source: N/A
    Relevance: N/A
    Progress:
    N/A
    Summary:
    N/A
    UPDATE HISTORY
    MORE LESS
    ICON STACK
    MESSAGE BOARD