Spring Security & Spring Session 实现单点登录
时间: 2024-08-16 07:09:04 浏览: 114
Spring Security 和 Spring Session 结合可以实现企业级应用的单点登录(Single Sign-On, SSO)功能。Spring Security 是一款广泛使用的开源安全框架,负责用户认证、授权等任务,而 Spring Session 则是 Spring 提供的一个轻量级会话管理解决方案。
在实现 SSO 的过程中,通常的做法是:
1. 用户首次登录时,通过 Spring Security 进行身份验证。如果成功,会在服务器端存储用户的 session(包括用户的标识信息),这个 session 会被关联到一个特定的登录名或 session ID。
2. 当用户从其他应用访问系统时,首先检查他们是否已经有一个有效的 session(例如通过 cookie 或 URL 参数传递)。如果没有,引导用户进入登录页面。
3. 如果有 session,Spring Security 会检查该 session 是否有效(例如过期时间),同时验证 session 中的用户信息。如果验证通过,直接视为已登录,跳过登录流程。
4. Spring Session 可以跨多个应用共享 session,使得用户只需要在一个应用中登录一次,就能在所有依赖 Spring Security 的应用中保持登录状态,实现了单点登录。
相关问题
springsecurity获取所有的session
要获取所有的session,你需要使用Spring Security提供的SessionRegistry接口。以下是获取所有活动session的示例代码:
```
@Autowired
private SessionRegistry sessionRegistry;
public List<Object> getAllSessions() {
List<Object> principals = sessionRegistry.getAllPrincipals();
List<Object> sessions = new ArrayList<>();
for (Object principal : principals) {
List<SessionInformation> sessionInformationList = sessionRegistry.getAllSessions(principal, false);
if (sessionInformationList != null && sessionInformationList.size() > 0) {
for (SessionInformation sessionInformation : sessionInformationList) {
sessions.add(sessionInformation.getSessionId());
}
}
}
return sessions;
}
```
这个方法首先使用`sessionRegistry.getAllPrincipals()`获取所有活动用户的Principal(即用户名),然后使用`sessionRegistry.getAllSessions(principal, false)`获取每个用户的所有session,并将它们的session ID添加到一个列表中。最后,该方法将这个列表返回。
请注意,这个方法只能在已经启用Spring Security的情况下使用,而且只能获取已经认证的用户的session。如果你想获取未认证用户的session,你需要使用Servlet API提供的方法来获取session。
springsecurity+jwt实现单点登录代码流程
Spring Security结合JWT实现单点登录的代码流程如下:
1. 创建一个Spring Boot项目并添加以下依赖:
```
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
```
2. 在Spring Security配置类中添加以下代码:
```
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
@Autowired
private CustomUserDetailsService customUserDetailsService;
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private int expiration;
@Bean
public JwtAuthenticationFilter authenticationTokenFilter() {
return new JwtAuthenticationFilter();
}
@Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.userDetailsService(customUserDetailsService)
.passwordEncoder(passwordEncoder());
}
@Bean(BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/auth/**")
.permitAll()
.anyRequest()
.authenticated();
// 添加JWT过滤器
http.addFilterBefore(authenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
public JwtTokenProvider jwtTokenProvider() {
return new JwtTokenProvider(secret, expiration);
}
}
```
3. 创建一个JwtTokenProvider类,实现JWT的生成、解析和验证:
```
@Service
public class JwtTokenProvider {
private final String secret;
private final long expiration;
@Autowired
public JwtTokenProvider(@Value("${jwt.secret}") String secret, @Value("${jwt.expiration}") long expiration) {
this.secret = secret;
this.expiration = expiration;
}
public String generateToken(Authentication authentication) {
UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
Date now = new Date();
Date expiryDate = new Date(now.getTime() + expiration);
return Jwts.builder()
.setSubject(Long.toString(userPrincipal.getId()))
.setIssuedAt(new Date())
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
public Long getUserIdFromToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
return Long.parseLong(claims.getSubject());
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
return true;
} catch (SignatureException ex) {
System.out.println("Invalid JWT signature");
} catch (MalformedJwtException ex) {
System.out.println("Invalid JWT token");
} catch (ExpiredJwtException ex) {
System.out.println("Expired JWT token");
} catch (UnsupportedJwtException ex) {
System.out.println("Unsupported JWT token");
} catch (IllegalArgumentException ex) {
System.out.println("JWT claims string is empty.");
}
return false;
}
}
```
4. 创建一个JwtAuthenticationFilter类,拦截请求并验证JWT:
```
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenProvider jwtTokenProvider;
@Autowired
private CustomUserDetailsService customUserDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && jwtTokenProvider.validateToken(jwt)) {
Long userId = jwtTokenProvider.getUserIdFromToken(jwt);
UserDetails userDetails = customUserDetailsService.loadUserById(userId);
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 in security context", 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, bearerToken.length());
}
return null;
}
}
```
5. 创建一个JwtAuthenticationEntryPoint类,处理未授权的请求:
```
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
```
6. 创建一个UserPrincipal类,实现Spring Security的UserDetails接口:
```
public class UserPrincipal implements UserDetails {
private Long id;
private String username;
private String email;
private String password;
private Collection<? extends GrantedAuthority> authorities;
public UserPrincipal(Long id, String username, String email, String password, Collection<? extends GrantedAuthority> authorities) {
this.id = id;
this.username = username;
this.email = email;
this.password = password;
this.authorities = authorities;
}
public Long getId() {
return id;
}
public String getEmail() {
return email;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
```
7. 创建一个CustomUserDetailsService类,实现Spring Security的UserDetailsService接口:
```
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
@Transactional
public UserDetails loadUserByUsername(String usernameOrEmail) throws UsernameNotFoundException {
User user = userRepository.findByUsernameOrEmail(usernameOrEmail, usernameOrEmail)
.orElseThrow(() ->
new UsernameNotFoundException("User not found with username or email : " + usernameOrEmail)
);
return UserPrincipal.create(user);
}
@Transactional
public UserDetails loadUserById(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() ->
new ResourceNotFoundException("User", "id", id)
);
return UserPrincipal.create(user);
}
}
```
以上就是Spring Security结合JWT实现单点登录的代码流程,可以根据具体的需求进行相应的修改和调整。
阅读全文