Spring Boot+Vue 앞 뒤 분리 항목 로그 인 한 사용 자 를 어떻게 제거 합 니까?
                                            
 15469 단어  springbootvue로그 인 한 사용 자 를 제거
                    
그러나 완벽 하지 않 은 점 이 있 습 니 다.바로 우리 의 사용 자 는 메모리 에 설 치 된 사용자 입 니 다.우 리 는 사용 자 를 데이터베이스 에 넣 지 않 았 습 니 다.정상 적 인 상황 에서 송 형 이 Spring Security 시리즈 에서 말 한 다른 설정 은 모두Spring Security+Spring Data Jpa 가 강하 게 손 을 잡 으 면 안전 관리 가 더 간단 합 니 다!글 을 참고 하여 데 이 터 를 데이터베이스 에 있 는 데이터 로 전환 하면 된다.
본 고 는 본 시리즈 의 13 편 으로 앞의 글 을 읽 으 면 본 고 를 더욱 잘 이해 하 는 데 도움 이 된다.
본 논문 의 사례 는Spring Security+Spring Data Jpa 의 강력 한 합작 을 바탕 으로 안전 관 리 는 더욱 간단 할 수 밖 에 없습니다!라 는 글 로 구 축 될 것 이기 때문에 중복 되 는 코드 는 쓰 지 않 겠 습 니 다.어린이 들 이 익숙 하지 않 으 면 이 글 을 참고 할 수 있 습 니 다.
1.환경 준비
먼저,우 리 는Spring Security+Spring Data Jpa 가 강하 게 손 을 잡 으 면 안전 관리 가 더 간단 합 니 다!글 의 사례 를 열 었 는데 이 사례 는 Spring Data Jpa 와 결합 하여 사용자 데 이 터 를 데이터베이스 에 저장 했다.
그리고 우 리 는 위의 글 에서 언급 된 로그 인 페이지 를 프로젝트 에 복사 합 니 다(문장 끝 에 전체 사례 를 다운로드 할 수 있 습 니 다).
[외부 체인 이미지 전송 에 실 패 했 습 니 다.원본 사이트 에 도 난 방지 체인 체제 가 있 을 수 있 습 니 다.그림 을 저장 하여 직접 업로드 하 는 것 을 권장 합 니 다(img-7XB0viq6-1588898082940)(http://img.itboyhub.com/2020/...]
그리고 Security Config 에서 로그 인 페이지 를 약간 설정 합 니 다:
@Override
public void configure(WebSecurity web) throws Exception {
 web.ignoring().antMatchers("/js/**", "/css/**", "/images/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
 http.authorizeRequests()
 ...
 .and()
 .formLogin()
 .loginPage("/login.html")
 .loginProcessingUrl("/doLogin")
 ...
 .and()
 .sessionManagement()
 .maximumSessions(1);
}자,설정 이 완료 되면 프로젝트 를 시작 하고 병렬 다 중 로그 인 테스트 를 시작 합 니 다.
여러 개의 브 라 우 저 를 열 고 각각 다단 계 로그 인 테스트 를 진행 합 니 다.우 리 는 모든 브 라 우 저가 로그 인 에 성공 할 수 있 고 로그 인 에 성공 할 때마다 이미 로그 인 한 사용 자 를 차 버 리 지 않 는 다 는 것 을 놀 라 게 발 견 했 습 니 다!
이거 어떻게 된 거 야?
2.문제 분석
이 문 제 를 알 기 위해 서 는 Spring Security 가 사용자 대상 과 session 을 어떻게 저장 하 는 지 알 아야 합 니 다.
Spring Security 에 서 는 Session Registry Impl 류 를 통 해 세 션 정보 에 대한 통일 적 인 관 리 를 실현 합 니 다.이러한 소스 코드(부분)를 살 펴 보 겠 습 니 다.
public class SessionRegistryImpl implements SessionRegistry,
 ApplicationListener<SessionDestroyedEvent> {
 /** <principal:Object,SessionIdSet> */
 private final ConcurrentMap<Object, Set<String>> principals;
 /** <sessionId:Object,SessionInformation> */
 private final Map<String, SessionInformation> sessionIds;
 public void registerNewSession(String sessionId, Object principal) {
 if (getSessionInformation(sessionId) != null) {
 removeSessionInformation(sessionId);
 }
 sessionIds.put(sessionId,
 new SessionInformation(principal, sessionId, new Date()));
 principals.compute(principal, (key, sessionsUsedByPrincipal) -> {
 if (sessionsUsedByPrincipal == null) {
 sessionsUsedByPrincipal = new CopyOnWriteArraySet<>();
 }
 sessionsUsedByPrincipal.add(sessionId);
 return sessionsUsedByPrincipal;
 });
 }
 public void removeSessionInformation(String sessionId) {
 SessionInformation info = getSessionInformation(sessionId);
 if (info == null) {
 return;
 }
 sessionIds.remove(sessionId);
 principals.computeIfPresent(info.getPrincipal(), (key, sessionsUsedByPrincipal) -> {
 sessionsUsedByPrincipal.remove(sessionId);
 if (sessionsUsedByPrincipal.isEmpty()) {
 sessionsUsedByPrincipal = null;
 }
 return sessionsUsedByPrincipal;
 });
 }
}메모리 기반 사용 자 를 사용 했다 면 Spring Security 의 정 의 를 살 펴 보 겠 습 니 다.
public class User implements UserDetails, CredentialsContainer {
 private String password;
 private final String username;
 private final Set<GrantedAuthority> authorities;
 private final boolean accountNonExpired;
 private final boolean accountNonLocked;
 private final boolean credentialsNonExpired;
 private final boolean enabled;
 @Override
 public boolean equals(Object rhs) {
 if (rhs instanceof User) {
 return username.equals(((User) rhs).username);
 }
 return false;
 }
 @Override
 public int hashCode() {
 return username.hashCode();
 }
}따라서 메모리 기반 사용 자 를 사용 할 때 문제 가 없 으 며 사용자 정의 사용 자 를 사용 하 는 데 문제 가 있 습 니 다.
문제 의 소 재 를 찾 으 면 문 제 를 해결 하 는 것 이 쉬 워 집 니 다.User 류 의 equals 방법 과 hashCode 방법 을 다시 쓰 면 됩 니 다.
@Entity(name = "t_user")
public class User implements UserDetails {
 @Id
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 private Long id;
 private String username;
 private String password;
 private boolean accountNonExpired;
 private boolean accountNonLocked;
 private boolean credentialsNonExpired;
 private boolean enabled;
 @ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.PERSIST)
 private List<Role> roles;
 @Override
 public boolean equals(Object o) {
 if (this == o) return true;
 if (o == null || getClass() != o.getClass()) return false;
 User user = (User) o;
 return Objects.equals(username, user.username);
 }
 @Override
 public int hashCode() {
 return Objects.hash(username);
 }
 ...
 ...
}Jpa 가 아 닌 MyBatis 를 사용 했다 면 로그 인 사용자 의 equals 방법 과 hashCode 방법 만 다시 쓰 면 됩 니 다.
3.마이크로 인사 응용
3.1 존재 하 는 문제점
마이크로 인 사 는 현재 JSON 형식 으로 로그 인 되 어 있 기 때문에 프로젝트 가 session 병발 수 를 제어 하면 추가 적 인 문제 가 발생 할 수 있 습 니 다.
가장 큰 문 제 는 사용자 정의 필터 로 UsernamePassword AuthenticationFilter 를 대체 하여 앞에서 말 한 session 에 대한 설정 을 모두 무효 화 하 는 것 입 니 다.모든 관련 설정 은 새 필터 LoginFilter 에서 설정 해 야 합 니 다.Session Authentication Strategy 를 포함 하여 수 동 으로 설정 해 야 합 니 다.
이 는 업 무량 을 가 져 왔 지만 끝나 면 Spring Security 에 대한 이해 가 한층 더 높 아 질 것 이 라 고 믿 습 니 다.
3.2 구체 적 인 응용
구체 적 으로 어떻게 실현 되 는 지 살 펴 보 겠 습 니 다.저 는 주로 관건 적 인 코드 를 열거 합 니 다.전체 코드 는 GitHub 에서 다운로드 할 수 있 습 니 다.https://github.com/lenve/vhr
먼저 첫 번 째 단 계 는 Hr 류 의 equals 와 hashCode 방법 을 다시 씁 니 다.다음 과 같 습 니 다.
public class Hr implements UserDetails {
 ...
 ...
 @Override
 public boolean equals(Object o) {
 if (this == o) return true;
 if (o == null || getClass() != o.getClass()) return false;
 Hr hr = (Hr) o;
 return Objects.equals(username, hr.username);
 }
 @Override
 public int hashCode() {
 return Objects.hash(username);
 }
 ...
 ...
}여기 서 저 희 는 Session AuthenticationStrategy 를 제공 해 야 합 니 다.앞에서 session 을 처리 하 는 것 은 Concurrent Session Control AuthenticationStrategy 입 니 다.즉,저 희 는 Concurrent Session Control AuthenticationStrategy 의 인 스 턴 스 를 제공 한 다음 에 Login Filter 에 배치 해 야 합 니 다.그러나 Concurrent Session Control Authentication Strategy 인 스 턴 스 를 만 드 는 과정 에서 Session Registry Impl 대상 이 필요 합 니 다.
앞에서 말 했 듯 이 Session Registry Impl 대상 은 세 션 정 보 를 유지 하 는 데 사 용 됩 니 다.현재 이 물건 도 저희 가 직접 제공 해 야 합 니 다.Session Registry Impl 인 스 턴 스 는 잘 만 들 어 졌 습 니 다.다음 과 같 습 니 다.
@Bean
SessionRegistryImpl sessionRegistry() {
 return new SessionRegistryImpl();
}
@Bean
LoginFilter loginFilter() throws Exception {
 LoginFilter loginFilter = new LoginFilter();
 loginFilter.setAuthenticationSuccessHandler((request, response, authentication) -> {
 //  
 }
 );
 loginFilter.setAuthenticationFailureHandler((request, response, exception) -> {
 //  
 }
 );
 loginFilter.setAuthenticationManager(authenticationManagerBean());
 loginFilter.setFilterProcessesUrl("/doLogin");
 ConcurrentSessionControlAuthenticationStrategy sessionStrategy = new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry());
 sessionStrategy.setMaximumSessions(1);
 loginFilter.setSessionAuthenticationStrategy(sessionStrategy);
 return loginFilter;
}사실상편에서 우리 의 배치 방안 도 결국 위 와 같이 지금 우리 스스로 이것 을 썼 을 뿐이다.
이것 괜찮아요?없다session 처리 에 또 하나의 관건 적 인 필 터 는 Concurrent Session Filter 라 고 합 니 다.원래 이 필 터 는 우리 가 관리 할 필요 가 없 었 지만 이 필터 에 도 Session Registry Impl 이 사용 되 었 습 니 다.Session Registry Impl 은 현재 우리 가 정의 하고 있 기 때문에 이 필 터 는 우리 도 다시 설정 해 야 합 니 다.다음 과 같 습 니 다.
@Override
protected void configure(HttpSecurity http) throws Exception {
 http.authorizeRequests()
 ...
 http.addFilterAt(new ConcurrentSessionFilter(sessionRegistry(), event -> {
 HttpServletResponse resp = event.getResponse();
 resp.setContentType("application/json;charset=utf-8");
 resp.setStatus(401);
 PrintWriter out = resp.getWriter();
 out.write(new ObjectMapper().writeValueAsString(RespBean.error("          ,       !")));
 out.flush();
 out.close();
 }), ConcurrentSessionFilter.class);
 http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);
}
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
 @Autowired
 SessionRegistry sessionRegistry;
 @Override
 public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
 //  
 UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
 username, password);
 setDetails(request, authRequest);
 Hr principal = new Hr();
 principal.setUsername(username);
 sessionRegistry.registerNewSession(request.getSession(true).getId(), principal);
 return this.getAuthenticationManager().authenticate(authRequest);
 } 
 ...
 ...
 }
}OK,그 후에 우리 의 프로젝트 설정 이 완성 되 었 습 니 다.
다음 에 vhr 프로젝트 를 다시 시작 하고 다단 계 로그 인 테스트 를 실시 합 니 다.만약 에 자신 이 오프라인 상태 에 빠 지면 다음 과 같은 힌트 를 볼 수 있 습 니 다.
 
 완전한 코드,나 는 이미 vhr 에 업데이트 되 었 으 니,여러분 은 다운로드 하여 공부 할 수 있 습 니 다.
4.소결
자,본 고 는 주로 어린이 들 과 Spring Security 에서 session 병발 문 제 를 처리 할 때 발생 할 수 있 는 구덩이 와 앞 뒤 가 분 리 된 상황 에서 session 병발 문 제 를 어떻게 처리 하 는 지 소개 했다.친구 들 이 GET 가 도 착 했 는 지 모 르 겠 어 요.
본 논문 의 두 번 째 소절 의 사례 는 GitHub 에서 다운로드 할 수 있 습 니 다.https://github.com/lenve/spring-security-samples
수확 이 있 으 면 골 라 서 송 이 형 을 격려 하 세 요~
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
thymeleaf로 HTML 페이지를 동적으로 만듭니다 (spring + gradle)지난번에는 에서 화면에 HTML을 표시했습니다. 이번에는 화면을 동적으로 움직여보고 싶기 때문에 입력한 문자를 화면에 표시시키고 싶습니다. 초보자의 비망록이므로 이상한 점 등 있으면 지적 받을 수 있으면 기쁩니다! ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.