下面是完整的SpringSecurity+Redis认证过程攻略。
准备工作
要进行SpringSecurity+Redis认证,我们需要先进行一些准备工作。具体包括:
- 搭建好Spring项目,并引入相应的依赖库,如SpringSecurity和Redis。
- 配置好SpringSecurity,包括配置安全过滤器、权限控制等内容。
- 安装配置好Redis,确保项目可以连接到Redis数据库。
默认情况下,SpringSecurity在进行认证时,会将用户信息放在Session中。但是Session中的数据是存放在内存中的,如果应用发生重启或者集群环境下,会导致认证信息丢失,因此需要使用Redis来存储认证信息。
认证过程
接下来,我们就来具体讲解SpringSecurity+Redis认证的过程。整个认证过程可以分为以下几个步骤:
- 用户登录,输入用户名和密码。
- SpringSecurity根据用户名查询并获取用户的信息。
- SpringSecurity将用户信息存储到Redis中,并生成token作为认证凭据。
- 将token返回给用户。
- 用户在后续的页面请求中需要携带该token,以证明自己的身份。
- 验证该token是否有效,有则可以访问受保护的资源,无则需要重新进行认证。
下面针对这些步骤进行详细的讲解。
1. 用户登录
用户输入用户名和密码,点击登录按钮进行登录操作。登录页面的代码示例如下:
<!DOCTYPE html>
<html>
<head>
<title>Login Page</title>
</head>
<body>
<h1>Login Page</h1>
<form method="post" action="/login">
<p>
<label>User Name:</label>
<input type="text" name="username" />
</p>
<p>
<label>Password:</label>
<input type="password" name="password" />
</p>
<button type="submit">Log in</button>
</form>
</body>
</html>
2. 获取用户信息
用户在登录页面输入用户名和密码后,SpringSecurity会根据用户名查询并获取用户的信息。这一步需要在自定义UserDetailsService实现类中完成。具体代码示例如下:
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("用户不存在!");
}
List<GrantedAuthority> authorities = new ArrayList<>();
// 为用户授权
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
return new org.springframework.security.core.userdetails.User(
user.getUsername(), user.getPassword(), authorities);
}
}
3. 存储用户信息到Redis
获取用户信息后,SpringSecurity会将用户信息存储到Redis中,并生成token作为认证凭据。生成token的代码示例如下:
@Service
public class TokenServiceImpl implements TokenService {
private static final long EXPIRATION_TIME = 60 * 60 * 24 * 7; // 1 week in seconds
private static final String SECRET = "ThisIsASecret"; // TODO: update this with real secret
@Override
public String generateToken(UserDetails user) {
Map<String, Object> claims = new HashMap<>();
claims.put("sub", user.getUsername());
claims.put("iat", new Date().getTime() / 1000);
claims.put("exp", new Date().getTime() / 1000 + EXPIRATION_TIME);
return Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
}
}
4. 返回token给用户
token生成后,SpringSecurity会将token返回给用户。示例代码如下:
@Service
public class TokenAuthenticationService {
private static final String AUTH_HEADER_NAME = "X-AUTH-TOKEN";
public void addTokenToResponse(HttpServletResponse response, String token) {
response.addHeader(AUTH_HEADER_NAME, token);
}
public Authentication getAuthentication(HttpServletRequest request) {
String token = request.getHeader(AUTH_HEADER_NAME);
if (token != null) {
// parse the token and extract the user details
UserDetails userDetails = getUserDetailsFromToken(token);
if (userDetails != null) {
return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
}
}
return null;
}
private UserDetails getUserDetailsFromToken(String token) {
try {
Jws<Claims> claims = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token);
String username = claims.getBody().getSubject();
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
// TODO: populate the authorities list based on the user roles
return new org.springframework.security.core.userdetails.User(
username, "", authorities);
} catch (JwtException ex) {
// TODO: handle the exception
}
return null;
}
}
5. 携带token访问资源
用户在后续的页面请求中需要携带该token,以证明自己的身份。示例代码如下:
curl -H "X-AUTH-TOKEN: eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ1c2VyMSIsImlhdCI6MTYwMzgxMzc2MywiZXhwIjoxNjAzODE4MjczfQ.m8HlDKBQKLP5Fa2CYQQatbJV5Rhif2HCJyVZtSxHTorFV6ZF8JL420kC-XtWZDG_Qd5I_Y0cg9ul1PDg0qApFQ" http://localhost:8080/api/user/1
6. 验证token
用户携带token访问受保护的资源时,需要验证该token是否有效,如果有效则可以访问资源,否则需要重新进行认证。示例代码如下:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyUserDetailsService userDetailsService;
@Autowired
private TokenAuthenticationService tokenAuthenticationService;
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/user/**").authenticated()
.anyRequest().permitAll()
.and()
.addFilterBefore(new TokenAuthenticationFilter(tokenAuthenticationService, userDetailsService, redisConnectionFactory), BasicAuthenticationFilter.class);
}
}
上述代码中,我们配置了一个TokenAuthenticationFilter类来验证token的有效性。TokenAuthenticationFilter的代码示例如下:
public class TokenAuthenticationFilter extends OncePerRequestFilter {
private static final Logger log = LoggerFactory.getLogger(TokenAuthenticationFilter.class);
private final TokenAuthenticationService tokenAuthenticationService;
private final UserDetailsService userDetailsService;
private final RedisConnectionFactory redisConnectionFactory;
public TokenAuthenticationFilter(TokenAuthenticationService tokenAuthenticationService, UserDetailsService userDetailsService, RedisConnectionFactory redisConnectionFactory) {
this.tokenAuthenticationService = tokenAuthenticationService;
this.userDetailsService = userDetailsService;
this.redisConnectionFactory = redisConnectionFactory;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = request.getHeader("X-AUTH-TOKEN");
if (token != null) {
String username = getUsernameFromToken(token);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (validateToken(token, userDetails)) {
TokenBasedAuthentication authentication = new TokenBasedAuthentication(userDetails, token);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
filterChain.doFilter(request, response);
}
private String getUsernameFromToken(String authToken) {
try {
return Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(authToken)
.getBody()
.getSubject();
} catch (JwtException ex) {
log.error("Error parsing token: {}", authToken);
}
return null;
}
private boolean validateToken(String token, UserDetails userDetails) {
String storedToken = redisConnectionFactory.getConnection().get(userDetails.getUsername());
return storedToken != null && storedToken.equals(token);
}
}
至此,SpringSecurity+Redis认证过程就讲解完了。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringSecurity+Redis认证过程小结 - Python技术站