Mastering RBAC and JWT with Spring Security: A Step‑by‑Step Guide
This comprehensive tutorial explains RBAC concepts, model classifications, and permission handling, then walks through implementing Spring Security with in‑memory authentication, JWT integration, custom JSON login filters, password encryption, and database‑backed authentication, providing complete code examples and configuration details.
RBAC Overview
Role‑Based Access Control (RBAC) assigns permissions to roles, and users inherit those permissions through role membership, simplifying permission management and reducing errors.
Model Classification
Four RBAC models are described:
RBAC0 : Basic model with users‑to‑roles and roles‑to‑permissions relationships.
RBAC1 : Adds role hierarchies (sub‑roles) for inheritance.
RBAC2 : Introduces constraints such as role separation, cardinality, and prerequisite roles.
RBAC3 : Combines RBAC1 and RBAC2 into a unified model.
Permissions and User Groups
Permissions represent access to resources (pages, APIs, data operations). Grouping users allows bulk role assignment, reducing administrative effort.
Spring Security – Simple Usage
Add the spring-boot-starter-security dependency to a Spring Boot project and create a basic controller:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency> 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 an in‑memory user:
@Configuration
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin").password("123").roles("ADMIN");
}
}Integrating JWT with Spring Security
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>Create a JwtUser class implementing UserDetails and a JwtTokenUtil utility for generating, parsing, and validating tokens.
public class JwtUser implements UserDetails {
private String username;
private String password;
private Integer state;
private Collection<? extends GrantedAuthority> authorities;
// constructors, getters, and overridden methods omitted for brevity
} public class JwtTokenUtil implements Serializable {
private String secret;
private Long expiration;
private String header;
// generateToken, getClaimsFromToken, getUsernameFromToken, isTokenExpired, refreshToken, validateToken methods
}Implement a JWT authentication filter that extracts the token from the request header, validates it, and sets the security context:
@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 = 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);
}
}Configure Spring Security to use the JWT filter and enforce stateless sessions:
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@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();
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}Custom JSON Login Filter
Override UsernamePasswordAuthenticationFilter to read JSON credentials from the request body:
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (MediaType.APPLICATION_JSON_VALUE.equals(request.getContentType()) ||
MediaType.APPLICATION_JSON_UTF8_VALUE.equals(request.getContentType())) {
try (InputStream is = request.getInputStream()) {
AuthenticationBean bean = new ObjectMapper().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);
}
}
return super.attemptAuthentication(request, response);
}
}Register the custom filter in the security configuration:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()
.authorizeRequests()
.antMatchers("/", "/login**").permitAll()
.anyRequest().authenticated()
.and().formLogin().loginPage("/")
.and().csrf().disable();
http.addFilterAt(customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
@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 verifying passwords:
@Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}Example service method for user registration and login:
@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 stored = userRepository.findByName(user.getName());
if (stored == null) return ResultInfo.error("用户名不存在");
if (!bCryptPasswordEncoder.matches(user.getPassword(), stored.getPassword()))
return ResultInfo.error("密码不正确");
return ResultInfo.success("登录成功");
}
}Database‑Backed Authentication
Configure a security class that uses a custom UserDetailsService backed by a database:
@Configurable
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
@Bean
public 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();
}
}The tutorial concludes by summarizing the RBAC concepts and demonstrating how to secure a Spring Boot application using in‑memory authentication, JWT tokens, custom JSON login, password hashing, and database‑driven 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.
IT Architects Alliance
Discussion and exchange on system, internet, large‑scale distributed, high‑availability, and high‑performance architectures, as well as big data, machine learning, AI, and architecture adjustments with internet technologies. Includes real‑world large‑scale architecture case studies. Open to architects who have ideas and enjoy sharing.
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.
