Integrating Spring Security with Spring Boot for JWT Authentication and Authorization
This article demonstrates how to integrate Spring Security into a Spring Boot application, configure JWT‑based authentication, implement custom AES encryption, define user and role entities, set up service and controller layers, and configure security, filter, and CORS settings to achieve secure login and permission management.
Spring Security is a powerful and highly customizable framework for authentication and access control. The article walks through integrating Spring Security with Spring Boot to achieve login authentication using JWT tokens and method‑level authorization.
1. Basic Concepts – JWT is used to generate a unique, time‑limited token for each user; the token expires based on business requirements. Spring Security checks the user’s role after login to determine access to protected resources.
2. Environment Preparation
CREATE TABLE `auth_user` (
`id` varchar(36) NOT NULL,
`username` varchar(100) DEFAULT NULL,
`password` varchar(100) DEFAULT NULL,
`role` varchar(100) DEFAULT NULL,
`account_non_expired` int(11) DEFAULT '0',
`account_non_locked` int(11) DEFAULT '0',
`credentials_non_expired` int(11) DEFAULT '0',
`is_enabled` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf32;
INSERT INTO auth_user (id, username, password, `role`, account_non_expired, account_non_locked, credentials_non_expired, is_enabled)
VALUES
('1', 'user', '15tT+y0b+lJq2HIKUjsvvg==', 'USER', 1, 1, 1, 1),
('2', 'admin', '15tT+y0b+lJq2HIKUjsvvg==', 'ADMIN', 1, 1, 1, 1);3. Project Dependencies
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- AES encryption -->
<dependency>
<groupId>org.apache.directory.studio</groupId>
<artifactId>org.apache.commons.codec</artifactId>
<version>1.8</version>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
</dependencies>4. Entity Class (AuthUser)
public class AuthUser implements Serializable, UserDetails {
private static final long serialVersionUID = 1L;
private String id;
private String username;
private String password;
private String role;
private Integer accountNonExpired;
private Integer accountNonLocked;
private Integer credentialsNonExpired;
private Integer isEnabled;
@Override
public Collection
getAuthorities() {
String[] roles = role.split(",");
List
authorities = new ArrayList<>();
for (String r : roles) {
authorities.add(new SimpleGrantedAuthority("ROLE_" + r));
}
return authorities;
}
@Override
public boolean isAccountNonExpired() { return accountNonExpired != null && accountNonExpired == 1; }
@Override
public boolean isAccountNonLocked() { return accountNonLocked != null && accountNonLocked == 1; }
@Override
public boolean isCredentialsNonExpired() { return credentialsNonExpired != null && credentialsNonExpired == 1; }
@Override
public boolean isEnabled() { return isEnabled != null && isEnabled == 1; }
// getters and setters omitted
}5. Service Layer
public interface AuthUserService extends UserDetailsService {}
@Service("authUserService")
public class AuthUserServiceImpl implements AuthUserService {
@Resource
private AuthUserDao authUserDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
AuthUser authUser = authUserDao.queryByName(username);
if (authUser == null) {
throw new IllegalArgumentException("User [" + username + "] doesn't exist.");
}
return authUser;
}
}6. Controller Example
@RestController
@RequestMapping("api/resource")
public class ResourceController {
@GetMapping("user")
public String demo1() { return "User demo."; }
@GetMapping("admin")
public String demo2() { return "Admin demo."; }
}7. AES Utility
public class AESUtil {
private static final String ALGORITHM = "AES/CBC/NoPadding";
private static final String DEFAULT_IV = "1234567890123456";
private static final String DEFAULT_KEY = "1234567890123456";
// encrypt / decrypt methods omitted for brevity
}8. JWT Utility (TokenUtil)
public class TokenUtil {
public static final String JWT_KEY = "ibudai";
public static final Long JWT_TTL = TimeUnit.MINUTES.toMillis(5);
public static String createJWT(String data, Long ttlMillis) {
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
JwtBuilder builder = getJwtBuilder(data, ttlMillis, uuid);
return builder.compact();
}
public static Claims parseJWT(String token) {
SecretKey secretKey = generalKey();
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();
}
private static SecretKey generalKey() {
byte[] encodedKey = Base64.getDecoder().decode(JWT_KEY);
return new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
}
private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
SignatureAlgorithm algorithm = SignatureAlgorithm.HS256;
SecretKey secretKey = generalKey();
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
if (ttlMillis == null) ttlMillis = JWT_TTL;
Date exp = new Date(nowMillis + ttlMillis);
return Jwts.builder()
.setId(uuid)
.setSubject(subject)
.setIssuer("budai")
.setIssuedAt(now)
.signWith(algorithm, secretKey)
.setExpiration(exp);
}
}9. Security Configuration
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AuthUserService authUserService;
@Value("${auth.api.free}")
private String freeAPI;
@Value("${auth.api.user}")
private String userAPI;
@Value("${auth.api.admin}")
private String adminAPI;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(authUserService)
.passwordEncoder(new AESEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
String[] free = freeAPI.trim().split(",");
String[] user = userAPI.trim().split(",");
String[] admin = adminAPI.trim().split(",");
http.authorizeRequests()
.antMatchers(free).permitAll()
.antMatchers(user).hasRole("USER")
.antMatchers(admin).hasRole("ADMIN")
.anyRequest().authenticated()
.and().formLogin().loginProcessingUrl("/api/auth/verify")
.successHandler(this::successHandle)
.failureHandler(this::failureHandle)
.and().exceptionHandling().authenticationEntryPoint(this::unAuthHandle)
.and().httpBasic()
.and().cors()
.and().csrf().disable();
}
// successHandle, failureHandle, unAuthHandle implementations omitted
}10. Custom Password Encoder (AESEncoder)
public class AESEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence charSequence) {
String str = charSequence.toString();
try {
String plain = Objects.equals(str, "userNotFoundPassword") ? str : AESUtil.desEncrypt(str);
return AESUtil.encrypt(plain);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public boolean matches(CharSequence charSequence, String s) {
try {
String plain = AESUtil.desEncrypt(charSequence.toString());
String result = AESUtil.encrypt(plain);
return Objects.equals(result, s);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}11. Filter Configuration
@Configuration
public class FilterConfig {
@Value("${auth.api.verify}")
private String verifyAPI;
@Bean
public FilterRegistrationBean
orderFilter1() {
FilterRegistrationBean
filter = new FilterRegistrationBean<>();
filter.setName("auth-filter");
filter.setUrlPatterns(Collections.singleton("/**"));
filter.addInitParameter("excludedUris", verifyAPI);
filter.setOrder(-1);
filter.setFilter(new AuthFilter());
return filter;
}
}12. AuthFilter Implementation
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse resp = (HttpServletResponse) servletResponse;
String token = req.getHeader("Token");
if (StringUtils.isNotBlank(token)) {
boolean expired = false;
try { TokenUtil.parseJWT(token); }
catch (ExpiredJwtException e) { expired = true; }
if (!expired) { filterChain.doFilter(req, servletResponse); return; }
resp.setStatus(203);
writeResult(resp, "Login expired.");
} else {
resp.setStatus(203);
writeResult(resp, "Please login and try again.");
}
}
private void writeResult(HttpServletResponse resp, String msg) throws IOException {
resp.setContentType("application/json;charset=UTF-8");
ResultData
result = new ResultData<>(203, msg, null);
resp.getWriter().write(objectMapper.writeValueAsString(result));
}13. CORS Configuration
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Value("${auth.host.cors}")
private String hosts;
@Override
public void addCorsMappings(CorsRegistry registry) {
String[] corsHosts = hosts.trim().split(",");
registry.addMapping("/**")
.allowedOriginPatterns(corsHosts)
.allowCredentials(true)
.allowedMethods("GET", "POST", "DELETE", "PUT")
.allowedHeaders("*")
.maxAge(TimeUnit.SECONDS.toMillis(5));
}
}The complete setup enables users to log in via the /api/auth/verify endpoint, receive a JWT token and a Base64‑encoded authentication string, and subsequently access protected resources according to their roles, while handling token expiration, authentication failures, and cross‑origin requests.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
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.