머리말
인기있는 일반 승인 프레임 워크에는 이제 Apache의 Shiro와 Spring Family Spring Security가 포함됩니다. 오늘날의 마이크로 서비스 인증에 관해서는 권한 부여 프레임 워크를 사용하여 자체 인증 서비스를 구축해야합니다. 오늘날 총리는 총리였습니다.
Spring Security는 주로 인증 (인증, 귀하의 솔루션은 누구입니까?) 및 액세스 제어 (액세스 제어, 즉, 허용되는 일, 권한 부여라고도 함)를 구현합니다. Spring Security는 인증 아키텍처에서 인증을 분리하고 확장 지점을 제공합니다.
핵심 물체
기본 코드는 Spring-Security-Core 패키지 아래에 있습니다. 스프링 보안을 이해하려면 내부의 핵심 물체에주의를 기울여야합니다.
SecurityContexTholder, SecurityContext 및 인증
SecurityContexTholder는 SecurityContext를위한 스토리지 컨테이너입니다. ThreadLocal Storage는 기본적으로 사용되므로 SecurityContext 메소드는 동일한 스레드에서 사용할 수 있습니다.
SecurityContext는 주로 응용 프로그램의 주요 정보를 저장하며 Spring Security의 인증으로 표시됩니다.
교장 얻기 :
Objectal = SecurityContexTholder.getContext (). getAuthentication (). getPrincipal (); if (userDetails) {String username = ((userDetails) principal) .getUserName ();} else {string username = principal.tostring ();};Spring Security에서는 인증 정의를 살펴볼 수 있습니다.
공개 인터페이스 인증은 원칙, 직렬화 가능한 {collection <? getedauthority> getAuthorities ()를 확장합니다. / *** 일반적으로 비밀번호*/ 객체 getCredentials (); /*** 인증 요청에 대한 추가 세부 정보를 저장합니다. 이들은 IP * 주소, 인증서 일련 번호 등 일 수 있습니다. */ 객체 getDetails (); /*** 인증 여부를 식별하는 데 사용됩니다. 사용자 이름과 비밀번호로 로그인하면 일반적으로 사용자 이름*/ 객체 getPrincipal (); / *** 인증 여부*/ boolean isauthenticated (); void setAuthenticated (부울 isauthenticated) 불법적 인 소송을 던진다;}실제 응용 분야에서 usernamepasswordauthenticationtoken은 일반적으로 사용됩니다.
공개 초록 클래스 AbstractAuthenticationToken은 인증, CredentityScontainer {} 공개 클래스 usernamepasswordauthenticationtoken 확장 actractAuthenticationToken {}를 확장합니다.일반적인 인증 프로세스는 일반적으로 다음과 같습니다. usernamepasswordauthenticationToken을 작성한 다음 인증을 위해 인증 관리자에게 넘겨주십시오 (나중에 자세히 설명). 인증이 전달되면 인증 정보가 SecurityContexTholder를 통해 저장됩니다.
usernamepasswordauthenticationtoken authenticationToken = new usernamepasswordauthenticationToken (loginvm.getusername (), loginvm.getPassword ()); Authentication Authentication = this.authenticationManager.authenticate (AuthenticationToken); SecurityContexTholder.getContext ();
userDetails 및 UserDetailsService
UserDetails는 Spring Security의 주요 인터페이스로, 교장을 나타내는 데 사용됩니다.
공개 인터페이스 userDetails는 직렬화 가능 { / *** 사용자 인증 정보를 역할* / collection <로 이해할 수 있습니다. getedauthority> getAuthorities ()를 확장합니다. / ** * 사용자 비밀번호 * * @비밀번호 */ String getPassword (); / *** 사용자 이름**/ String getUserName (); 부울 isaccountnonexpired (); 부울 isaccountnonlocked (); 부울 iscredentialsnonexpired (); 부울 isenabled ();}userDetails는 인증에 필요한 정보를 제공합니다. 실제로 사용하면 직접 사용자 데테일을 구현하고 이메일, 모바일 및 기타 정보와 같은 추가 정보를 추가 할 수 있습니다.
인증에서 교장은 일반적으로 사용자 이름입니다. userDetailsService를 통해 교장을 통해 사용자 데테일을 얻을 수 있습니다.
public interface userDetailsService {userDetails loadUserByUserName (String username)은 usernamenotfoundException;} 던지기부여 된
userDeTails에서 언급했듯이 Role_AdMinistrator 또는 Role_HR_SUPERVISOR와 같은 역할로 이해할 수 있습니다.
요약
인증 인증
AuthenticationManager
인증은 주로 AuthenticationManager 인터페이스를 통해 달성되며, 여기에는 하나의 메소드 만 포함됩니다.
Public Interface AuthenticationManager {인증 인증 (인증 인증)은 AuthenticationException;}Authenticate () 메소드는 주로 세 가지를 수행합니다.
AuthenticationException은 런타임 예외이며, 일반적으로 응용 프로그램에 의해 공통적 인 방식으로 처리됩니다. 사용자 코드는 일반적으로 포착 및 처리 할 필요가 없습니다.
AuthenticationManager의 기본 구현은 ProviderManager이며 인증을 구현하기 위해 AuthenticationProvider 인스턴스 세트를 위임합니다.
AuthenticationProvider 및 AuthenticationManager는 인증과 유사하지만 인증이 포함되어 있지만 발신자가 주어진 인증 유형을 지원하는지 여부를 쿼리 할 수있는 추가 메소드 지원이 있습니다.
Public Interface AuthenticationProvider {Authentication Authentication (Authentication Authentication)은 AuthenticationException을 던졌습니다. 부울 지원 (클래스 <?> 인증);}ProviderManager에는 AuthenticationProviders 세트가 포함되어 있습니다. 인증을 실행할 때 제공자를 가로지고 지원을 호출합니다. 지원되면 현재 제공 업체를 가로 지르는 인증 메소드를 실행합니다. 공급자가 성공적으로 인증 된 경우 중단하십시오.
공개 인증 인증 (인증 인증) AuthenticationException {class <? 인증 확장> totest = authentication.getClass (); AuthenticationException lastException = null; 인증 결과 = null; 부울 디버그 = logger.isdebugenabled (); for (authenticationProvider Provider : getProviders ()) {if (! provider.supports (totest)) {계속; } if (debug) {logger.debug ( "" + provider.getClass (). getName ())을 사용한 인증 시도; } try {result = provider.authenticate (인증); if (result! = null) {CopyDetails (인증, 결과); 부서지다; }} catch (ac // SEC-546 : AUTH 실패가 잘못된 계정 상태 THRIS e; } catch (internalauthenticationserViceException e) {prepereexception (e, 인증); e 던지기; } catch (AuthenticationException e) {lastException = e; }} if (result == null && parent! = null) {// 부모가 시도하도록 허용합니다. try {result = parent.authenticate (인증); } catch (providernotFoundException e) {// // 부모에게 전화하기 전에 다른 예외가 발생하지 않으면 아래에 던져 질 것입니다. // 부모와 부모를 호출하기 전에 // 아이의 제공자가 이미 처리 된 채로 ProviderNotFound를 던질 수 있습니다} catch e) {lastException = e; }} if (result! = null) {if (EraseCredentialSafterAuthentication && (result instanceof credentityScontainer)) {// 인증이 완료되었습니다. 인증에서 자격 증명 및 기타 비밀 데이터를 제거합니다 ((CredentityScontainer) 결과) .eraseCredentials (); } eventPublisher.publishauthenticationSuccess (결과); 반환 결과; } // 부모는 null이거나 인증하지 않았거나 예외를 던지지 않았습니다. if (lastException == NULL) {lastException = new ProviderNotFoundException (messages.getMessage ( "providerManager.ProvidEntFound", new Object [] {totest.getName ()}, "{0}"); } prepareexection (lastException, 인증); LastException을 던지십시오. }위 코드에서 볼 수 있듯이 ProviderManager에는 선택적 부모가 있습니다. 부모가 비어 있지 않으면 parent.authenticate (인증)가 호출됩니다
AuthenticationProvider
AuthenticationProvider에는 많은 구현이 있습니다. 당신이 가장 걱정하는 것은 일반적으로 daoauthenticationprovider이며, AbstractUserDetailSauthenticationProvider에서 상속됩니다. 핵심은 userDetails를 통해 인증을 구현하는 것입니다. daoauthenticationprovider는 기본적으로 자동로드되며 수동으로 구성 할 필요가 없습니다.
먼저 AbstractUserDetailSauthenticationProvider를 살펴보고 가장 핵심 인증을 살펴 보겠습니다.
공개 인증 인증 (인증 인증 인증)은 AuthenticationException을 던졌습니다. {// usernamepasswordauthenticationtoken assert.isinstance (usernamepasswordwordauthenticationtokn.class, authentication, messages.getMessage ( "AbstractUserDetailSauthenticationProvider.onlysupports", ",", ",", ",", ",", ",", "," 지원 "); // 사용자 이름 string username = (augentication.getPrincipal () == null)을 가져옵니다. "none_provided": Authentication.getName (); 부울 cachewasused = true; // 캐시에서 userDetails를 가져옵니다. user = this.usercache.getUserFromCache (username); if (user == null) {cachewasused = false; 시도 {// user user = retrieveUser (username, (usernamepasswordauthenticationToken) 인증을 얻기 위해 retrieveUser Abstract 메소드를 시도합니다. } catch (usernamenotfoundException notFound) {logger.debug ( "user '" + username + "'찾을 수 없음"); if (HideUserNotFoundExceptions) {Throw New BadCredentialSexection (것으로스 getMessage ( "AbstractUserDetailSauthenticationProvider.badcredentials", "나쁜 자격 증명")); } else {Throw NotFound; }} assert.notnull (user, "retrieveUser retured null- 인터페이스 계약 위반"); } try {// pre-Check, defaultPreauthenticationChecks, 사용자가 잠겨 있는지 또는 preauthenticationChecks.Check (user)에 대한 계정을 사용할 수 있는지 확인하십시오. // 초록 방법, 사용자 정의 검사 추가 조정 체크 (user, (usernamepasswordauthenticationToken) 인증); } catch (authenticationException Exception) {if (cachewasied) {// 문제가 있으므로 확인 후 다시 시도하십시오. // 우리는 최신 데이터를 사용하고 있습니다 (즉, 캐시에서 제외) CacheWasused = false; user = retrieveUser (username, (usernamepasswordauthenticationtoken) 인증); preauthenticationChecks.Check (사용자); 추가 AuthenticationChecks (user, (usernamepasswordauthenticationtoken) 인증); } else {던지기 예외; }} // 후 확인 후 DefaultPostAuthenticationChecks, iscredentialSnOnexpired PostAuthenticationChecks.Check (user); if (! cachewasused) {this.usercache.putuserincache (user); } Object PrincipalToreturn = 사용자; if (ForcePrincipAlastring) {PrincipalToreturn = user.getUserName (); } return returnesuccessAuthentication (PrincipalToreturn, 인증, 사용자); }위의 테스트는 주로 사용자 획득 및 검증 로직이 특정 클래스에서 구현되는 UserDetails 구현을 기반으로합니다. 기본 구현은 daoauthenticationprovider입니다. 이 클래스의 핵심은 개발자가 userDetailsService를 제공하여 userDetails 및 PasswordEncoder를 얻기 위해 암호가 유효한 지 확인할 수 있도록하는 것입니다.
private userDetailsService userDetailSService; Private PasswordEncoder passwordencoder;
특정 구현을 보려면 검색 사용자가 직접 userDetailsService에 전화하여 사용자를 얻습니다.
보호 된 최종 사용자 데일 테일 검색 사용자 (String username, usernamepasswordauthenticationtoken authentication) authenticationException {userDetails loadEduser; try {loadeDuser = this.getUserDetailsService (). loadUserByUserName (username); } catch (usernamenotfoundException notFound) {if (authentication.getCredentials ()! = null) {String presentedPassword = augentication.getCredentials (). toString (); passwordencoder.ispasswordvalid (usernotfoundencodedpassword, presentedpassword, null); } 던지기를 던지십시오. } catch (Exception RepositoryProblem) {Throw New NewnerAuthenticationServiceException (repositoryProblem.getMessage (), repositoryProblem); } if (loadEdUser == null) {새로운 내부 AuthenticationSerViceException ( "userDetailSService returned null, 이는 인터페이스 계약 위반 인"); } return loadEduser; }확인을 살펴 보겠습니다.
보호 된 void 추가 AutheTenticationChecks (userDetails userDetails, usernamepasswordauthenticationTokenTokenTokenTokenTokenTokenTokenToking) authenticationException {object salt = null; if (this.saltsource! = null) {salt = this.saltsource.getSalt (userDetails); } if (augentication.getCredentials () == null) {logger.debug ( "인증 실패 : 자격 증명이 제공 없음"); 새로운 badcredentialsexception을 던지십시오 (messages.getMessage ( "AbstractUserDetailSauthenticationProvider.badcredentials", "나쁜 자격 증명")); } // 사용자 비밀번호를 가져옵니다. string presentedPassword = augentication.getCredentials (). toString (); // passwordencoder 이후의 비밀번호가 userDetails의 비밀번호와 동일한 지 여부를 비교합니다. if (! passwordEncoder.ispasswordValid (userDetails.getPassword (), presentedpassword, salt)) {logger.debug ( "인증 실패 : 비밀번호가 저장된 값과 일치하지 않습니다"); 새로운 badcredentialsexception을 던지십시오 (messages.getMessage ( "AbstractUserDetailSauthenticationProvider.badcredentials", "나쁜 자격 증명")); }}요약 : 인증을 사용자 정의하려면 daoauthenticationProvider를 사용하면 암호 에코 데이더 및 사용자 데일 스며들만 제공하면됩니다.
인증 관리자를 사용자 정의하십시오
Spring Security는 건축업자 클래스 AuthenticationManagerBuilder를 제공하여 사용자 정의 인증을 신속하게 구현할 수 있습니다.
공식 소스 코드 설명을 참조하십시오.
SecurityBuilder는 AuthenticationManager를 작성하는 데 사용되었습니다. 메모리 인증, LDAP 인증, JDBC 기반 인증, userDetailsService 추가 및 AuthenticationProvider의 추가에서 쉽게 구축 할 수 있습니다.
AuthenticationManagerBuilder는 메모리 기반 인증, LDAP 인증, JDBC 인증을 생성하고 userDetailSService 및 AuthenticationProvider를 추가 할 수있는 AuthenticationManager를 작성하는 데 사용될 수 있습니다.
간단한 사용 :
@configuration@enablewebsecurity@enableglobalmethodsecurity (prepostenabled = true, securedenabled = true) public class applicationsecurity 확장 websecurityConfigerAdapter {public securityConfiguration (AuthenticationManagerBuilder authenticationManagerBuilder, userDetailService userdetsorvice, tokenprov). TokenProvider, Corsfilter Corsfilter, SecurityProblemsUpport 문제화 업적) {this.authenticationManagerBuilder = AuthenticationManagerBuilder; this.userDetailsService = userDetailsService; this.tokenprovider = TokenProvider; this.corsfilter = corsfilter; this.problemsupport = 문제업자; } @postConstruct public void init () {try {authenticationManagerBuilder .UserDetailSService (userDetailSService) .PasswordEncoder (passwordEncoder ()); } catch (예외 e) {새 beaninitializationException 던지기 ( "보안 구성 실패", e); }}} @override protected void configure (httpsecurity http)는 예외를 {http .addfilterbefore (corsfilter, usernamepasswordauthenticationfilter.class) .exception handling () .authenticationentrypoint (problurepport) .accesseniedhandler (). .Headers () .FrameOptions () .disable (). and () .sessionManagement () .sessionCreationPolicy (SessionCreationPolicy.stationeless () .AuthorizeRequests () .AntMatchers ( "/API/register") .ANTMatchers ( "/API/Activate"). .AntMatchers ( "/api/authenticate"). permitAll () .AntMatchers ( "/api/account/reset-password/init"). cermitAll () .AntMatchers ( "/api/account/reset-password/finish"). permitall () .antMatchers ( "/api/profile-info"). .AntMatchers ( "/api/**"). Authenticated () .AntMatchers ( "/Management/Health"). permitAll () .AntMatchers ( "/manage/**"). HASAUTHORITY (hasAuthority (AgrisitiesConstants.Admin) .AntMatchers ( "/v2/api-docs/**"). .AntMatchers ( "/swagger-resources/configuration/ui"). permitAll () .AntMatchers ( "/swagger-ui/index.html"). hasauthority (hasauthority (resinitiesconstants.admin). 및 (securityConfigerAdapter ()); }}승인 및 액세스 제어
인증이 성공하면 AccessDecisionManager를 통해 구현되는 승인을 계속할 수 있습니다. 프레임 워크에는 세 가지 구현이 있습니다. 기본값은 긍정적 인 기반이며, 이는 AccessDecisionVoTer를 통해 만들어졌으며, 이는 제공자 관리자가 인증을 위해 AuthenticationProviders에 맡겨진 것과 비슷합니다.
공개 무효 결정 (인증 인증, 객체 객체, 수집 <configattribute> configattributes)은 AccessDeniedException {int deny = 0; // (accessDecisionVoTer 유권자 : getDecisionVoTers ()) {// 투표 int result = vote.vote (인증, 개체, configattributes); if (logger.isdebugenabled ()) {logger.debug ( "voter :" + vover + ", 반환 :" + result); } switch (결과) {case accessDecisionVoTer.Access_granted : return; CASE ACCESSDECISIONVOTER.ACCESS_DENIED : DENY ++; 부서지다; 기본값 : 브레이크; }} // 거부권 if (deny> 0) {wrach new accessDeniedexception (messages.getMessage ( "acpractAccessDecisionManager.AccessDenied", "Access Denies")); } // 이것을 멀리 얻으려면 모든 AccessDecisionVoTer가 기권합니다. }AccessDecisionVoTer를 살펴 보겠습니다.
부울 지원 (configattribute attribute); 부울 지원 (클래스 <?> clazz); int vote (인증 인증, s 객체, collection <configattribute> 속성);
객체는 사용자가 액세스하려는 리소스이며 구성 요소는 객체를 충족 해야하는 조건입니다. 일반적으로 페이로드는 role_admin과 같은 문자열입니다. Rolevoter의 구현을 살펴 보겠습니다. 핵심은 인증에서 wrantedauthority를 추출한 다음 조건이 충족되는지 구성원과 비교하는 것입니다.
public boolean supports (configattribute attribute) {if ((attribute.getAttribute ()! = null) && attribute.getAttribute (). startSwith (getRolePrefix ())) {return true; } else {return false; }} public boolean supports (class <?> clazz) {return true; } public int vote (인증 인증, 객체 객체, 수집 <configattribute> attributes) {if (authentication == null) {return access_denied; } int result = access_abstain; // GRINGEDAUTHORITY 정보 COLLECTION <? grantauthority> 당국 = 추출물 (인증)을 확장합니다. for (configattribute 속성 : 속성) {if (this.supports (attribute)) {// 기본적으로 거부 된 access result = access_denied; // (GranteDauthority Authority : 당국)에 대한 부여 된 권한을 찾으려고 시도하려고 시도 {// 일치하는 권한이 있는지 확인하십시오 (attribute.getAttribute (). }}}} 반환 결과; }여기서 나는 configattribute가 어디에서 왔습니까? 실제로, 그것은 위의 ApplicationSecurity의 구성에 있습니다.
웹 보안을 구현하는 방법
웹 계층의 스프링 보안 (UI 및 HTTP 백엔드의 경우)은 서블릿 필터를 기반으로하며 다음 그림은 단일 HTTP 요청에 대한 전형적인 핸들러 계층을 보여줍니다.
Spring Security는 FilterChainProxy를 통해 단일 필터, 프록시 내부 필터로 웹 계층에 등록됩니다.
FilterChainProxy는 필터 컨테이너와 동일합니다. VirtualFilterChain을 통해 각 내부 필터는 순서대로 호출됩니다.
public void dofilter (servletrequest 요청, servletreponse 응답, 필터 체인 체인) ioexception, servletexception {boolean clearcontext = request.getAttribute (filter_applied) == null; if (clareContext) {try {request.setAttribute (filter_applied, boolean.true); Dofilterinternal (요청, 응답, 체인); } 마침내 {SecurityContexTholder.clearContext (); request.removeattribute (filter_applied); }} else {dofilterinternal (요청, 응답, 체인); }} private void dofilterinternal (servletrequest 요청, ServletRepronse 응답, 필터 체인)은 ioexception, servletexception {firewalledRequest fwrequest = firewall .getFireWalledRequest ((httpservletRequest) 요청); httpservletresponse fwrosponse = firewall .getFireWalledResponse ((httpservletResponse) 응답); List <filter> 필터 = GetFilters (fwRequest); if (filters == null || filters.size () == 0) {if (logger.isdebugenabled ()) {logger.debug (urlutils.BuildRequestUrl (fwRequest) + (filters == null? ":"빈 필터 목록이 있습니다 "); } fwrequest.reset (); Chain.dofilter (fwrequest, fwrosponse); 반품; } VirtualFilterChain VFC = New VirtualFilterChain (FwRequest, 체인, 필터); vfc.dofilter (fwrequest, fwrosponse); } 개인 정적 클래스 VirtualFilterChain은 FilterChain을 구현합니다. {개인 최종 필터 체인 원본 체인; 개인 최종 목록 <필터> 추가 필터; Private Final FirewalledRequest FirewalledRequest; 개인 최종 INT 크기; 개인 int currentPosition = 0; Private VirtualFilterChain (FireWalledRequest FirewalledRequest, Filterchain Chain, List <filter> 추가 필터) {this.originalchain = 체인; this.additionalfilters = 추가 필터; this.size = accestfilters.size (); this.firewalledRequest = FireWalledRequest; } public void dofilter (ServletRequest 요청, ServletResponse 응답)는 ioException, servleTeXception {if (currentPosition == size) {if (logger.isdebugenabled ()) {logger.debug (urlutils.buildrequesturl (Firewaldrequest) + "추가 파일 체인에 도달했습니다. } // 보안 필터 체인을 종료 할 때 경로 스트라이핑을 비활성화합니다. OriginalChain.dofilter (요청, 응답); } else {currentPosition ++; Filter NextFilter = 추가 필터 (currentPosition -1); if (logger.isdebugenabled ()) {logger.debug (urlutils.buildRequesturl (FirewalledRequest) + "At At" + currentPosition + "의 추가 필터 체인에서" + currentPosition + "의" + currentPosition + "; } nextFilter.dofilter (요청, 응답, this); }}}}참조하십시오
https://spring.io/guides/topicals/spring-security-architecture/
https://docs.spring.io/spring-security/site/docs/5.0.5.release/reference/htmlsingle/#overall-architecture
요약
위는이 기사의 전체 내용입니다. 이 기사의 내용에 모든 사람의 연구 나 작업에 대한 특정 참조 가치가 있기를 바랍니다. 궁금한 점이 있으면 의사 소통을 위해 메시지를 남길 수 있습니다. Wulin.com을 지원 해주셔서 감사합니다.