首先,我们需要确保具备以下的环境:
- JDK 1.8+
- Maven
- IntelliJ IDEA(或其他IDE)
接下来,我们可以按照以下步骤进行SpringBoot整合SpringSecurity实现认证拦截:
步骤一:创建SpringBoot工程
我们可以使用SpringBoot官方提供的Spring Initializr
来创建工程,也可以使用IDEA的New Project
功能创建工程。
步骤二:添加依赖
我们需要在pom.xml
文件中添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
步骤三:配置Spring Security
我们需要在SpringBoot工程中添加一个SecurityConfig
类来配置Spring Security:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password(passwordEncoder().encode("password")).roles("USER");
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
以上是一个简单的Spring Security配置,其中:
configure(HttpSecurity http)
方法配置了访问控制规则,首页和/home
页面可以访问,其他页面需要认证之后才能访问;configureGlobal(AuthenticationManagerBuilder auth)
方法配置了用户名和密码,这里我们使用了内存认证方式;passwordEncoder()
方法配置了密码加密方式,这里我们使用了BCryptPasswordEncoder
加密方式。
步骤四:添加登录页面和处理登录请求的Controller
我们需要添加一个登录页面和一个Controller来处理登录请求,代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login Page</title>
</head>
<body>
<form th:action="@{/login}" method="post">
<div>
<label>Username</label>
<input type="text" name="username"/>
</div>
<div>
<label>Password</label>
<input type="password" name="password"/>
</div>
<div>
<button type="submit">Sign in</button>
</div>
</form>
</body>
</html>
@Controller
public class LoginController {
@GetMapping("/login")
public String login() {
return "login";
}
@PostMapping("/login")
public String doLogin() {
return "redirect:/index";
}
@GetMapping("/index")
public String index() {
return "index";
}
}
以上代码中,login.html
是登录页面,LoginController
是处理登录请求的Controller,doLogin()
方法返回redirect:/index
,当登录成功后会跳转到/index
页面。
现在,我们可以启动SpringBoot工程,访问http://localhost:8080/home
页面,可以看到Spring Security的默认登录页面,输入用户名user
和密码password
,登录成功后会跳转到/index
页面。
这是一个简单的实现过程,接下来我们将会使用两个示例来进一步展示SpringBoot整合SpringSecurity实现认证拦截。
示例一:基于数据库认证
我们将会使用MySQL数据库来存储用户信息和角色信息,并通过JdbcUserDetailsManager
来实现用户认证。
首先,我们需要创建一个数据库和表:
CREATE DATABASE spring_security_demo;
USE spring_security_demo;
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(100) NOT NULL
);
CREATE TABLE roles (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL
);
CREATE TABLE user_roles (
user_id INT,
role_id INT,
PRIMARY KEY (user_id, role_id),
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (role_id) REFERENCES roles(id)
);
然后,我们需要添加依赖:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-data</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
其中,${mysql.version}
是MySQL JDBC驱动的版本号。
接下来,我们需要在application.properties
文件中添加数据库连接和认证相关的配置:
spring.datasource.url=jdbc:mysql://localhost:3306/spring_security_demo
spring.datasource.username=root
spring.datasource.password=123456
spring.security.user.name=user
spring.security.user.password=password
需要注意的是,spring.security.user.name
和spring.security.user.password
是Spring Security内存认证的默认配置,可以不设置。
然后,我们需要在SecurityConfig
类中修改configureGlobal(AuthenticationManagerBuilder auth)
方法,使用JdbcUserDetailsManager
认证:
@Autowired
DataSource dataSource;
@Autowired
PasswordEncoder passwordEncoder;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
JdbcUserDetailsManager userDetailsManager = new JdbcUserDetailsManager(dataSource);
if (!userDetailsManager.userExists("user")) {
userDetailsManager.createUser(
User.withUsername("user")
.password(passwordEncoder.encode("password"))
.roles("USER")
.build());
}
auth.userDetailsService(userDetailsManager).passwordEncoder(passwordEncoder);
}
以上代码中,我们首先使用JdbcUserDetailsManager
和DataSource
进行初始化,然后判断是否已存在用户名为user
的用户,如果不存在则创建该用户,最后使用userDetailsService()
和passwordEncoder()
配置用户认证和密码加密方式。
现在,我们可以启动SpringBoot工程,访问http://localhost:8080/home
页面,输入用户名user
和密码password
,登录成功后会跳转到/index
页面。
示例二:基于JWT认证
我们将会使用JWT来实现用户认证,JWT是一种轻量级的跨域认证方案。
首先,我们需要添加依赖:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jjwt.version}</version>
</dependency>
其中,${jjwt.version}
是JJWT的版本号。
然后,我们需要在application.properties
文件中添加JWT相关的配置:
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://localhost:9090/oauth2/.well-known/jwks.json
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:9090/oauth2/token
jwt.secret=mysecret
jwt.expiration-in-ms=1800000
以上代码中,spring.security.oauth2.resourceserver.jwt.jwk-set-uri
是JWT公钥的URL,spring.security.oauth2.resourceserver.jwt.issuer-uri
是JWT发行者的URL,这些信息可从认证服务器获取;jwt.secret
是JWT私钥,jwt.expiration-in-ms
是JWT过期时间。
下面是单点登录的示例:
@Configuration
@EnableWebSecurity
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
}
以上代码中,@EnableOAuth2Sso
注解即启用了单点登录。
接下来,我们需要创建一个JwtProvider
类来生成和解析JWT:
@Component
public class JwtProvider {
@Value("${jwt.secret}")
private String jwtSecret;
@Value("${jwt.expiration-in-ms}")
private int jwtExpirationInMs;
public String generateToken(Authentication authentication) {
User principal = (User) authentication.getPrincipal();
Date now = new Date();
Date expiration = new Date(now.getTime() + jwtExpirationInMs);
return Jwts.builder()
.setSubject(principal.getUsername())
.setIssuedAt(now)
.setExpiration(expiration)
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token);
return true;
} catch (SignatureException | MalformedJwtException | ExpiredJwtException | UnsupportedJwtException | IllegalArgumentException exception) {
return false;
}
}
public String getUsernameFromToken(String token) {
Claims claims = Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody();
return claims.getSubject();
}
}
以上代码中,generateToken()
方法用于生成JWT,validateToken()
方法用于校验JWT是否有效,getUsernameFromToken()
方法用于从JWT中获取用户名。
然后,我们需要添加一个JwtAuthenticationFilter
类:
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtProvider jwtProvider;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && jwtProvider.validateToken(jwt)) {
String username = jwtProvider.getUsernameFromToken(jwt);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ex) {
logger.error("Could not set user authentication", ex);
}
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
以上代码中,doFilterInternal()
方法在每次请求前都会调用,用于对JWT进行校验,如果JWT有效则设置当前用户的认证状态;getJwtFromRequest()
方法用于从HTTP请求头中获取JWT。
最后,我们需要在SecurityConfig
类中添加JWT认证相关的配置:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().antMatchers("/login/**", "/signup/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
以上代码中,我们使用jwt
前缀来标识JWT,将不需要进行认证的URL添加到permitAll()
中,使用addFilterBefore()
方法添加JwtAuthenticationFilter
到Spring Security过滤链中,并将其设置在用户名密码认证过滤器之前。
现在,我们可以启动SpringBoot工程,访问http://localhost:8080/home
页面,输入用户名user
和密码password
,登录成功后会生成一个JWT,将其携带在请求头中即可访问其他受保护的资源。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringBoot整合SpringSecurity实现认证拦截的教程 - Python技术站