Mastering RBAC with Spring Security and JWT: A Complete Guide
This tutorial walks through RBAC fundamentals, model classifications (RBAC0‑3), permission and user‑group concepts, then demonstrates practical Spring Security setups including in‑memory authentication, JWT integration, JSON‑based login, password encryption with BCrypt, and database‑backed authentication, providing complete code examples.
RBAC Permission Analysis
RBAC (Role‑Based Access Control) is a permission model that assigns permissions to roles and users to roles, simplifying management and reducing security risks.
Mind Map
What is RBAC
RBAC defines roles, users, and permissions; a user can have multiple roles, and a role can have multiple permissions, allowing efficient permission management.
Model Classification
RBAC0
Basic model with user‑to‑role and role‑to‑permission relationships. Example: a user may hold both administrative and financial roles.
RBAC1
Introduces role hierarchy (inheritance) and child roles.
RBAC2
Adds constraints such as mutually exclusive roles, cardinality limits, prerequisites, and runtime separation of duties.
RBAC3
Combines RBAC0‑2 features into a unified model.
What is Permission
Permission is a set of resources that can be accessed or manipulated, such as page access, CRUD operations, etc.
User Group Usage
User groups allow batch assignment of roles to many users, simplifying large‑scale permission management.
Spring Security Basic Usage
Add the Spring Security starter dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>Create a simple controller:
package com.example.demo.web;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/test")
public class Test {
@RequestMapping("/test")
public String test() {
return "test";
}
}Configure in‑memory authentication:
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin").password("123").roles("admin");
}
}HttpSecurity example to permit all requests:
http.authorizeRequests()
.anyRequest().permitAll()
.and()
.formLogin().permitAll()
.and()
.logout().permitAll();Spring Security + JWT Integration
Add JWT and Spring Security dependencies:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.3.1.RELEASE</version>
</dependency>Implement JwtUser that implements UserDetails, a JwtTokenUtil for token generation/validation, a JwtAuthenticationTokenFilter to extract the token, and a custom UserDetailsService.
package com.example.demo;
public class JwtUser implements UserDetails {
private String username;
private String password;
private Integer state;
private Collection<? extends GrantedAuthority> authorities;
// constructors, getters, and overridden methods...
} package com.example.demo;
public class JwtTokenUtil implements Serializable {
private String secret;
private Long expiration;
// generateToken, getUsernameFromToken, isTokenExpired, refreshToken, validateToken...
}Filter example:
package com.example.demo;
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired private UserDetailsService userDetailsService;
@Autowired private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
String authHeader = request.getHeader(jwtTokenUtil.getHeader());
if (authHeader != null && StringUtils.isNotEmpty(authHeader)) {
String username = jwtTokenUtil.getUsernameFromToken(authHeader);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(authHeader, userDetails)) {
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
chain.doFilter(request, response);
}
}Security configuration to use the filter and stateless sessions:
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
@Autowired private UserDetailsService userDetailsService;
@Autowired private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/auth/**").permitAll()
.anyRequest().authenticated()
.and().headers().cacheControl();
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
}
}JSON Login with Custom Filter
Override UsernamePasswordAuthenticationFilter to read JSON payload:
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
if (MediaType.APPLICATION_JSON_VALUE.equals(request.getContentType())) {
ObjectMapper mapper = new ObjectMapper();
try (InputStream is = request.getInputStream()) {
AuthenticationBean bean = mapper.readValue(is, AuthenticationBean.class);
UsernamePasswordAuthenticationToken authRequest =
new UsernamePasswordAuthenticationToken(bean.getUsername(), bean.getPassword());
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
} catch (IOException e) {
return super.attemptAuthentication(request, response);
}
} else {
return super.attemptAuthentication(request, response);
}
}
}Register the custom filter in the security config:
@Bean
public CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
filter.setAuthenticationSuccessHandler(new SuccessHandler());
filter.setAuthenticationFailureHandler(new FailureHandler());
filter.setFilterProcessesUrl("/login/self");
filter.setAuthenticationManager(authenticationManagerBean());
return filter;
}Password Encryption
Define a BCryptPasswordEncoder bean and use it when storing or checking passwords:
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}Service example:
@Service
public class UserServiceImpl implements UserService {
@Resource private UserRepository userRepository;
@Resource private BCryptPasswordEncoder bCryptPasswordEncoder;
public User add(User user) {
user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
return userRepository.save(user);
}
public ResultInfo login(User user) {
User dbUser = userRepository.findByName(user.getName());
if (dbUser == null) {
return new ResultInfo("-1", "用户名不存在");
}
if (!bCryptPasswordEncoder.matches(user.getPassword(), dbUser.getPassword())) {
return new ResultInfo("-1", "密码不正确");
}
return new ResultInfo("0", "登录成功");
}
}Database Authentication
Configure Spring Security to load users from the database and protect admin URLs:
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired private UserService userService;
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("admin")
.anyRequest().authenticated()
.and().formLogin()
.loginProcessingUrl("/login").permitAll()
.and().csrf().disable();
}
}In summary, the article explains RBAC concepts, various RBAC models, permission and user‑group management, and demonstrates how to implement authentication and authorization in Spring Boot using Spring Security, JWT, JSON login, password encryption, and database‑backed user details.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Su San Talks Tech
Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
