问题描述
我正在使用 RedisHttpSession,我的基本目标是在成功登录时将人员对象保存在会话对象中,在需要的任何地方检索它并在注销时销毁会话。
成功登录后,这就是我正在做的:
Staff staff = staffService.getEmailInstance(body.getEmailId());
request.getSession(true).setAttribute("staff",staff);
注销就是这样:
request.getSession().invalidate();
在不同的控制器中,我调用此实用程序方法来检查员工是否已登录:util.isstaffLoggedIn(request,response,StaffRole.EDITOR);
如果员工已登录,则 API 继续,否则用户将被重定向到登录页面。
@Service
public class Util {
public boolean isstaffLoggedIn(HttpServletRequest request,HttpServletResponse response,StaffRole staffRole)
throws PaperTrueInvalidCredentialsException,PaperTrueJavaException {
Staff staff = (Staff) request.getSession().getAttribute("staff");
if (!isObjectNull(staff) && staff.getStaffRole().equals(staffRole)) {
return true;
}
invalidateSessionAndRedirect(request,response);
return false;
}
public void invalidateSessionAndRedirect(HttpServletRequest request,HttpServletResponse response)
throws PaperTrueJavaException,PaperTrueInvalidCredentialsException {
request.getSession().invalidate();
try {
response.sendRedirect(ProjectConfigurations.configMap.get("staff_logout_path"));
} catch (IOException e) {
throw new PaperTrueJavaException(e.getMessage());
}
throw new PaperTrueInvalidCredentialsException("Staff not loggedIn");
}
}
现在,当应用运行时,get-jobs
API 会在成功登录后立即调用。大多数情况下,request.getSession().getAttribute("staff")
方法工作正常并返回“staff”对象,但偶尔会返回 null
。这并不经常发生,但确实发生了。我打印了会话 ID 以查看注销后它们是否不同,并且确实如此。每次注销后,我都有一个新的会话 ID。我什至检查了我从数据库中检索到的员工对象是否为空,但不是。
人员对象已成功保存在会话中,但我无法在其他 API 中检索它。这是我的会话配置的样子:
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 10800)
public class SessionConfig {
HashMap<String,String> configMap = ProjectConfigurations.configMap;
@Bean
public LettuceConnectionFactory connectionFactory() {
int redisPort = Integer.parseInt(configMap.get("redis_port"));
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(
configMap.get("redis_host"),redisPort);
redisStandaloneConfiguration.setPassword(configMap.get("redis_password"));
return new LettuceConnectionFactory(redisStandaloneConfiguration);
}
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("PTSESSIONID");
serializer.setSameSite("none");
serializer.setUseSecureCookie(!configMap.get("staff_logout_path").contains("localhost"));
return serializer;
}
}
如果我遗漏了什么,请告诉我。提前致谢。
更新 1
我不再使会话无效,而是将 request.getSession(true).setAttribute("staff",staff);
替换为 request.getSession().setAttribute("staff",staff);
我正在 StaffController 中设置“staff”并在 EditorController 中获取它。这是我的设置方式:
@RestController
@RequestMapping(path = { "/staff" },produces = "application/json")
public class StaffApiController {
private final HttpServletRequest request;
private final HttpSession httpSession;
@Autowired
private StaffService staffService;
@Autowired
StaffApiController(HttpServletRequest request,HttpSession session) {
this.request = request;
this.httpSession = session;
}
@PostMapping("/login")
public ResponseEntity<StaffLoginResponse> login(@Valid @RequestBody StaffLoginBody body) {
StaffLoginResponse staffLoginResponse = new StaffLoginResponse();
try {
if (!staffService.isValidLogin(body.getEmailId(),body.getpassword())) {
throw new PaperTrueInvalidCredentialsException("Invalid Credentials");
}
Staff staff = staffService.getEmailInstance(body.getEmailId());
httpSession.setAttribute("staff",staff);
staffLoginResponse.setEmail(staff.getEmail()).setRole(staff.getStaffRole().getValue())
.setStaffID(staff.getId()).setStatus(new Status("Staff Login Successful"));
} catch (PaperTrueException e) {
httpSession.removeAttribute("staff");
staffLoginResponse.setStatus(new Status(e.getCode(),e.getMessage()));
}
return ResponseEntity.ok(staffLoginResponse);
}
@PostMapping("/logout")
public ResponseEntity<Status> logout() {
httpSession.removeAttribute("staff");
return ResponseEntity.ok(new Status("Staff Logged Out Successfully"));
}
}
解决方法
如果您使用的是 Spring Security,您可以创建一个自定义的“/login”端点,通过设置 SecurityContext
来验证用户的身份。
您可以使用 Spring Security 提供的默认注销行为。
如果不需要在正文中提供凭据,则可以使用 Spring Security 提供的默认登录行为并完全省略此 Controller。
这是一个起点。
它不提供全面的安全性,例如它可能是易受攻击的会话固定攻击。
@RestController
public class LoginController {
private AuthenticationManager authenticationManager;
public LoginController(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@PostMapping("/login")
public void login(@RequestBody StaffLoginBody body,HttpServletRequest request) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(body.getUsername(),body.getPassword());
Authentication auth = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(auth);
HttpSession session = request.getSession();
session.setAttribute("staff","staff_value");
}
@GetMapping("/jobs")
public String getStaffJobs(HttpServletRequest request) {
return request.getSession().getAttribute("staff").toString();
}
}
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// expose AuthenticationManager bean to be used in Controller
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorize -> authorize
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
)
// use built in logout
.logout(logout -> logout
.deleteCookies("PTSESSIONID")
);
}
}
您需要添加 Spring Security 依赖项才能使用此代码 org.springframework.boot:spring-boot-starter-security
。