Vorwort
Zu den beliebten General Authorization Frameworks gehören nun das Shiro von Apache und die Spring Family Spring Security. Wenn es um die heutige Microservice -Authentifizierung geht, müssen wir unseren Autorisierungsrahmen verwenden, um unsere eigenen Authentifizierungsdienste zu erstellen. Heute war der Premierminister Premierminister.
Die Spring Security implementiert hauptsächlich die Authentifizierung (Authentifizierung, Lösung Wer sind Sie?) Und Zugangskontrolle (Zugriffskontrolle, dh was dürfen Sie tun? Auch als Autorisierung bezeichnet). Spring Security trennt die Authentifizierung von der Autorisierungsarchitektur und liefert Erweiterungspunkte.
Kernobjekte
Der Hauptcode befindet sich unter dem Paket für das Feder-Security-Core. Um Frühlingssicherheit zu verstehen, müssen Sie auf die Kernobjekte im Inneren achten.
SecurityContexTHolder, SecurityContext und Authentifizierung
SecurityContexTHolder ist ein Speichercontainer für SecurityContext. ThreadLocal -Speicher wird standardmäßig verwendet, was bedeutet, dass SecurityContext -Methoden im selben Thread verfügbar sind.
SecurityContext speichert hauptsächlich die Hauptinformationen der Anwendung und wird durch Authentifizierung in der Frühjahrssicherheit dargestellt.
Principal erhalten:
Objekt Princip = SecurityContextHolder.getContext (). GetAuthentication (). GetPrincipal (); if (Principal InstanceOf UserDetails) {String userername = ((userDetails).In der Frühlingssicherheit können Sie sich die Authentifizierungsdefinition ansehen:
Public Interface Authentifizierung erweitert Principal, Serializierbar {Sammlung <? erweitert GrantedAuthority> GetAuthorities (); / *** Normalerweise Passwort*/ Object GetCredentials (); /*** speichert zusätzliche Details zur Authentifizierungsanforderung. Dies können eine IP * -Ade, eine Seriennummer usw. Zertifikat usw. sein. */ Object getDetails (); /*** wird verwendet, um festzustellen, ob es authentifiziert ist. Wenn Sie sich mit einem Benutzernamen und einem Passwort anmelden, ist es normalerweise der Benutzername*/ Object GetPrincipal (). / *** ob es authentifiziert ist*/ boolean isauthenticated (); void setAuthenticated (boolean isauthenticated) löst illegalArgumentException aus;}In praktischen Anwendungen wird normalerweise usernamepasswordAuthenticationToken verwendet:
public Abstract Class AbstractAuthenticationToken implementiert Authentifizierung, CredentialsContainer {} öffentliche Klasse usernamepasswordAuthenticationToken erweitert die AbstractAuthenticationToken {}Ein gemeinsamer Authentifizierungsprozess ist normalerweise wie folgt: Erstellen Sie eine usernamepasswordAuthenticationToken und geben Sie es dann an die Authentifizierung für Authentifizierung (detailliert) an den AuthentifizierungSmanager übergeben. Wenn die Authentifizierung übergeben wird, werden die Authentifizierungsinformationen über den SecurityContexTHolder gespeichert.
UserernamepasswordAuthenticationToken AuthenticationToken = Neue usernamePasswordAuthenticationToken (loginvm.getUnername (), loginvm.getPassword ();
Benutzerdetails und BenutzerdetailsService
UserDetails ist eine wichtige Schnittstelle in der Frühjahrssicherheit, mit der ein Auftraggeber dargestellt wird.
Public Interface UserDetails erweitert serialisierbar { / *** Benutzerautorisierungsinformationen können als Rolle verstanden werden* / collection <? erweitert GrantedAuthority> GetAuthorities (); / ** * Benutzerkennwort * * @return das Passwort */ String getPassword (); / *** Benutzername**/ String getUnername (); boolean iscountnonexpired (); boolean iscountnonlocked (); boolean is credentialsNonexpired (); boolean isenabled ();}UserDetails liefert die erforderlichen Informationen für die Authentifizierung. Im tatsächlichen Gebrauch können Sie Benutzerdetails selbst implementieren und zusätzliche Informationen hinzufügen, z. B. E -Mail-, Mobil- und andere Informationen.
In der Authentifizierung ist Principal normalerweise der Benutzername. Wir können Benutzerdetails über das Principal über UserdetailsService erhalten:
public interface userDetailsService {userDetails loadUserByusername (String -Benutzername) löst usernamenotfoundException aus;}Zugegeben
Wie in UserDetails erwähnt, kann die GrantedAuthority als Rolle wie Rollen- oder Rollen- oder Rollen-_HR_Supervisor verstanden werden.
Zusammenfassung
Authentifizierungszertifizierung
AuthenticationManager
Die Authentifizierung wird hauptsächlich durch die AuthenticationManager -Schnittstelle erreicht, die nur eine Methode enthält:
Public Interface AuthenticationManager {Authentifizierungsauthentifizierung (Authentifizierungsauthentifizierung) löst die Authentifizierungspflicht aus;}Die Authenticate () -Methode macht hauptsächlich drei Dinge:
Die AuthentifizierungException ist eine Laufzeitausnahme, die normalerweise von der Anwendung auf gemeinsame Weise behandelt wird. Der Benutzercode muss normalerweise nicht speziell gefangen und verarbeitet werden.
Die Standardimplementierung von AuthenticationManager ist ProviderManager, der eine Reihe von Authentifizierungsprovider -Instanzen zur Implementierung der Authentifizierung delegiert.
Die AuthentifizierungProvider und AuthenticationManager ähneln der Authentifizierung. Beide enthalten Authentifizierung, bietet jedoch eine zusätzliche Methodenunterstützung, um die Abfrage zu ermöglichen, ob der Anrufer einen bestimmten Authentifizierungstyp unterstützt:
public interface authenticationProvider {Authentifizierungsauthentifizierung (Authentifizierungsauthentifizierung) löst die Authentifizierungspflicht aus; boolean unterstützt (Klasse <?> Authentifizierung);}Providermanager enthält eine Reihe von Authentifizierungspfleiern. Bei der Ausführung der Authentifizierung durchquert sie Anbieter und ruft dann Support auf. Wenn es unterstützt wird, wird die authentifizierende Methode ausgeführt, die den aktuellen Anbieter durchquert. Wenn ein Anbieter erfolgreich authentifiziert wird, brechen Sie.
öffentliche Authentifizierung Authentifizierung (Authentifizierungsauthentifizierung) löst die Authentifizierungsexzeption {Klasse <? erweitert Authentifizierung> TOTest = Authentifizierung.getClass (); AuthenticationException lastException = null; Authentifizierungsergebnis = null; boolean debug = logger.isdebugenabled (); für (AuthenticationProvider Anbieter: getProviders ()) {if (! provider.supports (wotest)) {Fortsetzung; } if (debug) {logger.debug ("Authentifizierungsversuch mit" + provider.getClass (). getName ()); } try {result = provider.authenticate (Authentifizierung); if (result! = null) {CopyDetails (Authentifizierung, Ergebnis); brechen; }} catch (account // Sec-546: Vermeiden Sie zusätzliche Anbieter, wenn ein Auth-Fehler auf // Ungültiger Kontostatuswurf zurückzuführen ist. } catch (InternalAuthenticationServiceException e) {PrepareException (e, Authentifizierung); werfen e; } catch (authenticationException e) {lastException = e; }} if (result == null && parent! = null) {// Erlaube dem Elternteil, es zu versuchen. try {result = parent.authenticate (Authentifizierung); } catch (ProvidernotFoundException e) {// Ignorieren Sie, wie wir unten werfen, wenn keine andere Ausnahme vor // übergeordnetes Anrufen und der Elternteil // ProviDernotfound abwerfen kann, obwohl ein Anbieter im Kind bereits die Anfrage bearbeitet} catche (Authentifizierung E) {lastException = e; }} if (result! = null) {if (eraseCredentialsFterAuthentication && (Ergebnisinstanz von Anmeldeinformationen)) {// Authentifizierung ist vollständig. Entfernen Sie Anmeldeinformationen und andere geheime Daten // aus der Authentifizierung (((Anmeldeinformat) .eraseScedential (); } eventPublisher.publishAuthenticationCcess (Ergebnis); Rückgabeergebnis; } // übergeordnet war null oder hat sich nicht authentifiziert (oder eine Ausnahme ausgelöst). if (lastException == null) {lastException = new ProvidernotFoundException (messages.getMessage ("ProviderManager.providernotfound", New Object [] {Totest.getName ()}, "keine AuthentifizierungProvider für {0}"); } prepareException (LastException, Authentifizierung); LastException werfen; }Wie aus dem obigen Code ersichtlich ist, hat Providermanager einen optionalen Elternteil. Wenn der Elternteil nicht leer ist, wird übergeordnet. Authenticate (Authentifizierung) wird aufgerufen
AuthenticationProvider
AuthenticationProvider verfügt über viele Implementierungen. Derjenige, über den Sie am meisten besorgt sind, ist normalerweise DaoAuthenticationProvider, der von AbstractUserDetailsAuthenticationProvider geerbt wurde. Der Kern besteht darin, die Authentifizierung über Benutzerdetails zu implementieren. DaoAuthenticationProvider wird standardmäßig automatisch geladen und muss nicht manuell konfiguriert werden.
Schauen wir uns zunächst die AbstractUserDetailsAuthenticationProvider an und schauen Sie uns die wichtigste Authentifizierung an:
Public Authentication Authentication (Authentifizierung Authentifizierung) löst die Authentifizierungsexzeption aus. unterstützt ")); // Benutzername String username = (Authentifizierung.getPrincipal () == NULL)? "None_Provided": Authentifizierung.getName (); boolean cachewasused = true; // Benutzerdetails von cache user = this.usercache.getUserfromcache (Benutzername) abrufen; if (user == null) {cachewasused = false; Versuchen Sie {// Abruft -Abstract -Methode abrufen, um user user = reagievUser (Benutzername, (userernAmepasswordAuthenticationToken) zu erhalten); } catch (usustamenotFoundException NotFound) {logger.debug ("Benutzer '" + Benutzername + "nicht gefunden"); if (hideUnernotFoundExceptions) {neue badcredentialSexception (message.getMessage ("AbstractUserDetailsAuthenticationProvider.Badcredentials", "Bademal Credeins")); } else {throw NotFound; }} Assert.notnull (Benutzer, "AbrufUser NULL zurückgegeben - eine Verstöße gegen den Schnittstellenvertrag"); } try {// Pre-Check, defaultPreautauthenticationChecks, prüfen Sie, ob der Benutzer gesperrt ist oder ob das Konto für PreauthenticationChecks.Check (Benutzer) verfügbar ist. // Abstract -Methode, benutzerdefinierte zusätzliche AuthenticationChecks (Benutzer, (usernamepasswordAuthenticationToken) Authentifizierung); } catch (AuthenticationException -Ausnahme) {if (cachewasused) {// Es gab ein Problem. Versuchen Sie also erneut, nachdem wir die neuesten Daten (dh nicht aus dem Cache) cachewasused = false verwenden; user = retrieveUser (Benutzername, (userernamepasswordAuthenticationToken) Authentifizierung); preauthenticationChecks.Check (Benutzer); zusätzliche AuthenticationChecks (Benutzer, (userernamepasswordAuthenticationToken) Authentifizierung); } else {Ausnahme werfen; }} // post-Check defaultPostAuthenticationChecks, prüfen Sie, ob die postautautautautautouthenticationChecks.Check (Benutzer); If (! } Objekt PrincipalToreturn = Benutzer; if (forcePrincipalassstring) {PrincipalTorTurn = user.getUnername (); } return createsuccessAuthentication (PrincipalTorTurn, Authentifizierung, Benutzer); }Der obige Test basiert hauptsächlich auf der Implementierung von Benutzerdetails, wobei die Benutzerakquisitions- und Überprüfungslogik von bestimmten Klassen implementiert wird. Die Standardimplementierung ist DaoAuthenticationProvider. Der Kern dieser Klasse besteht darin, den Entwicklern zu ermöglichen, BenutzerdetailsService bereitzustellen, um Benutzerdetails und PasswordEncoder zu erhalten, um zu überprüfen, ob das Passwort gültig ist:
private userdetailsService userDetailsService; private passwordEncoder passwordEncoder;
Um die spezifische Implementierung anzuzeigen, rufen Sie userDetailsService direkt auf, um den Benutzer zu erhalten:
geschützte endgültige Benutzerdetails AbrufUser (String -Benutzername, userernamePasswordAuthenticationToken -Authentifizierung) löst die Authentifizierung aus. try {LoadedUser = this.getUserDetailsService (). LoadUserByUserName (Benutzername); } catch (usternamenotFoundException NotFound) {if (authentication.getCredentials ()! PassageCoder.ispasswordValid (userernotFoundCodedPassword, präsentiertesPassword, null); } wirf nicht entfunden; } catch (Exception RepositoryProblem) {neue interneAuthenticationServiceException (repositoryProblem.getMessage (), repositoryProblem); } if (LoadedUser == null) {neue interneAuthenticationServiceException werfen ("userDetailsService zurückgegeben NULL, was eine Verstärkung des Schnittstellenvertrags ist"); } return LoadedUser; }Schauen wir uns die Überprüfung an:
Protected void zusätzlichAuthenticationChecks (UserDetails UserDetails, userernamePasswordAuthenticationToken -Authentifizierung) löst die Authentifizierungsexception aus {Object Salt = null; if (this.SaltSource! } if (authentication.getCredentials () == null) {logger.debug ("Authentifizierung fehlgeschlagen: Keine Anmeldeinformationen angegeben"); Neue badcredentialSexception werfen (message.getMessage ("AbstractUserDetailsAuthenticationProvider.Badcredentials", "Bad Referenzen")); } // Die Benutzerkennwortzeichenfolge präsentiertspassword = authentication.getCredentials (). ToString (); // Vergleichen Sie, ob das Kennwort nach PasswordEnCoder das gleiche wie das Kennwort von Benutzerdetails ist, wenn (! PasswordEnsCoder.ispasswordValid (userdetails.getPassword (), präsentiertesPass, salz) {logger.debug ("Authentifizierung fehlgeschlagen: Kennwort entspricht nicht dem gespeicherten Wert"). Neue badcredentialSexception werfen (message.getMessage ("AbstractUserDetailsAuthenticationProvider.Badcredentials", "Bad Referenzen")); }}Zusammenfassung: Um die Authentifizierung anzupassen, verwenden Sie DaoAuthenticationProvider, müssen Sie sie nur mit PasswordEnCoder und UserDetailsService zur Verfügung stellen.
Authentifizierungsmanager anpassen
Spring Security bietet eine Builder -Klasse -AuthentifizierungManagerBuilder, mit der Sie schnell benutzerdefinierte Authentifizierung implementieren können.
Siehe die Beschreibung der offiziellen Quellcode:
SecurityBuilder erstellte früher einen Authentifizierungsmanager. Ermöglicht das einfache Erstellen der Speicherauthentifizierung, die LDAP -Authentifizierung, die JDBC -basierte Authentifizierung, das Hinzufügen von BenutzerdetailsService und Hinzufügen von Authentifizierungsprovider.
Die AuthenticationManagerBuilder kann verwendet werden, um einen Authentifizierungsmanager zu erstellen, mit dem Speicher-basierte Authentifizierung, LDAP-Authentifizierung, JDBC-Authentifizierung erstellt und BenutzerdetailsService und Authentifizierungsprovider hinzugefügt werden können.
Einfache Verwendung:
@Configuration@EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)public class ApplicationSecurity extends WebSecurityConfigurerAdapter { public SecurityConfiguration(AuthenticationManagerBuilder authenticationManagerBuilder, UserDetailsService userDetailsService,TokenProvider TokenProvider, Corsfilter CorsFilter, SecurityProblemSupport problemsupport) {this.authenticationManagerBuilder = AuthenticationManagerBuilder; this.userDetailsService = userDetailsService; this.tokenProvider = tokenProvider; this.corsfilter = corsFilter; this.Problemsupport = problemsupport; } @Postconstruct public void init () {try {authenticationManagerBuilder .userDetailsService (userDetailsService) .PasswordEnCoder (passwordEncoder ()); } catch (Ausnahme E) {neue BeanInitializationException ("Sicherheitskonfiguration fehlgeschlagen", e); }} @Override Protected void configure (httpecurity http) löst Ausnahme aus {http .addfilterbefore (corsfilter, userernamepasswordAuthenticationFilter.class). AusceptionHandling () .AuthenticationStypeSportePort (). .Headers () .frameOptions () .Disable () .and () .SessionManagement (). .AntMatchers ("/api/authenticate"). GenehmigungsAll () .AntMatchers ("/api/account/reset-password/init"). GenehmigungsAll () .AntMatchers ("/api/account/account/reset-password/fundierung"). erlaubtenAll () .AntMatchers ("/api/profil-info (). .AntMatchers ("/Management/Health"). Genug () .AntMatchers ("/Management/**"). .AntMatchers ("/Swagger-ui/index.html"). }}Autorisierung und Zugangskontrolle
Sobald die Authentifizierung erfolgreich ist, können wir weiterhin autorisieren, was über AccessDecisionManager implementiert wird. Es gibt drei Implementierungen des Frameworks, der Standard wird bejahend, was über AccessDecisionvoter hergestellt wird, was ein bisschen wie der ProviderManager der Authentifizierungsprüfer zur Authentifizierung anvertraut ist.
public void Entscheidung (Authentifizierungsauthentifizierung, Objektobjekt, Sammlung <configAttribute> configAttributes) löst AccessDdenException {int deny = 0; // Traversal DecisionVoter für (AccessDecisionVoter -Wähler: getDeCisionVoters ()) {// Voting int result = stimmen.Vote (Authentifizierung, Objekt, configAttributes); if (logger.isdebugenabled ()) {logger.debug ("Wähler:" + Wähler + ", zurückgegeben:" + result); } switch (result) {case accessdecisionVoter.access_granted: return; case AccessDecisionVoter.Access_Denied: Deny ++; brechen; Standard: Break; }} // veto if (Deny> 0) {Neue AccessDdenEdException (message.getMessage ("AbstractAccessDecisionManager.AccessDenied", "Access verweigert"); } // Um so weit zu kommen, hat jeder AccessDecisionVoter -enthielt CheckallowifallabstaNdecisions (); }Schauen wir uns AccessDecisionvoter an:
boolean unterstützt (configAttribute -Attribut); boolean unterstützt (Klasse <?> clazz); int Stimme (Authentifizierungsauthentifizierung, S -Objekt, Sammlung <configAttribute> Attribute);
Das Objekt ist die Ressource, auf die der Benutzer zugreifen möchte, und ConfigAttribute ist die Bedingung, dass das Objekt erfüllt werden muss. Normalerweise ist die Nutzlast eine Zeichenfolge wie ROLE_ADMIN. Schauen wir uns also die Implementierung von Rolevoter an. Der Kern besteht darin, die GrantedAuthority aus der Authentifizierung zu extrahieren und dann mit configAttribute zu vergleichen, ob die Bedingungen erfüllt sind.
public boolean unterstützt (configAttribute -Attribut) {if (((Attribute.GetAtTribute)! } else {return false; }} public boolean unterstützt (Klasse <?> Clazz) {return true; } public int Vote (Authentifizierungsauthentifizierung, Objektobjekt, Sammlung <configAttribute> Attribute) {if (authentifizierung == null) {return access_denied; } int result = access_abstain; // Erhalten Sie die InformationScollection <? erweitert GrantedAuthority> Behörden = ExtractAuthorities (Authentifizierung); für (configAttribute attribut: Attribute) {if (this.supports (Attribut)) {// Zugriff, die standardmäßig abgelehnt wurde, result = access_denied; // Versuch, eine übereinstimmende Autorität für (gewährte Autorität: Behörden) zu finden. {// Bestimmen Sie, ob es eine passende Behörde gibt, wenn (Attribut.GetAttribute (). Equals (Authority.getAuthority ()) {// Sie können auf return_granted zugreifen; }}}} Rückgabeergebnis; }Hier muss ich fragen, woher kommt ConfigTattribute? Tatsächlich liegt es in der oben genannten Konfiguration der Anwendungen.
So implementieren Sie Websicherheit
Die Frühlingssicherheit (für UI und HTTP -Backends) in der Webschicht basiert auf Servlet -Filtern, und die folgende Abbildung zeigt eine typische Hierarchie von Handlern für eine einzelne HTTP -Anforderung.
Die Frühlingssicherheit ist über FilterchainProxy als einzelner Filter, Filter im Proxy, in der Webschicht registriert.
FilterchainProxy entspricht einem Filterbehälter. Durch VirtualFilterchain wird jeder interne Filter nacheinander aufgerufen.
public void dofilter (servletRequest request, servletResponse antwort, filterchain kette) löscht ioException, servletException {boolean clearContext = request.getAttribute (filter_applied) == null; if (clearContext) {try {request.setAttribute (filter_applied, boolean.true); dofilterinternal (Anfrage, Antwort, Kette); } endlich {SecurityContextHolder.ClearContext (); request.removeAttribute (filter_applied); }} else {dofilterInternal (Anfrage, Antwort, Kette); }} private void dofilterInternal (ServletRequest Request, ServletResponse -Antwort, Filterchain -Kette) löst IOException, ServletException aus {FirewalledRequest fWRequest = Firewall .GetFirewalledRequest ((httpserVletrequest)); HttpServletResponse fWresponse = Firewall .GetFireWalledResponse ((httpServletResponse) Antwort); Liste <Filter> filters = getFilters (fWRequest); if (filter == null || filters.size () == 0) {if (logger.isdebugenabled ()) {logger.debug (urlutils.buildRequesturl (fwRequest) + (filter == null? } fwRequest.reset (); chain.dofilter (fwRequest, fWresponse); zurückkehren; } VirtualFilterChain vfc = new VirtualFilterChain (fWRequest, Kette, Filter); vfc.dofilter (fwRequest, fWresponse); } private statische Klasse VirtualFilterChain implementiert Filterchain {private endgültige Filterchain OriginalChain; private endgültige Liste <filter> Zusätzliche Dateien; Private Final FirewalledRequest FirewalledRequest; private endgültige intgröße; private int currentPosition = 0; private virtualFilterchain (FirewalledRequest FirewalledRequest, Filterchain -Kette, Liste <Filter> Zusätzliche Filters) {this.originalchain = Kette; this.additionalFilters = zusätzliche Filters; this.size = zusätzliche filters.size (); this.firewalledRequest = FirewalledRequest; } public void dofilter (ServletRequest -Anfrage, ServletResponse -Antwort) löst IoException aus, servletException {if (currentPosition == Größe) {if (logger.isdebugenabled () {logger.debug (URLUTILS.BUILDREQUESTURLL (WIRDREWALLEDRECTEDREGEL) (URLUTILS.BUILDREQUESTURL (FIREWALLEDREGEDREGEL). } // Deaktivieren Sie die Pfadstreifen, während wir die Sicherheitsfilterkette beenden. OriginalChain.dofilter (Anfrage, Antwort); } else {currentPosition ++; Filter NextFilter = zusätzliche filters.get (Stromposition - 1); if (logger.isdebugenabled ()) {logger.debug (urlutils.buildRequesturl (fIREWALLEDREQUEST) + "Bei Position" + CurrentPosition + "von" + size + "in zusätzlicher Filterkette; Schussfilter:" + nextFilter.getClass (). } NextFilter.dofilter (Anfrage, Antwort, dies); }}}}beziehen sich auf
https://spring.io/guides/topicals/spring-security-architecture/
https://docs.spring.io/spring-security/site/docs/5.0..release/reference/htmlsingle/#overall-architecture
Zusammenfassen
Das obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, dass der Inhalt dieses Artikels einen gewissen Referenzwert für das Studium oder die Arbeit eines jeden hat. Wenn Sie Fragen haben, können Sie eine Nachricht zur Kommunikation überlassen. Vielen Dank für Ihre Unterstützung bei Wulin.com.