Hier übernehmen wir hauptsächlich die benutzerdefinierte Anmerkungslösung von Shiro. Dieser Artikel löst hauptsächlich die folgenden Probleme.
Wie man die Seite durch Logik mit der API-Schnittstelle verknüpft.
Verwendung von Shiros Selbstanmerkung.
So schreiben Sie benutzerdefinierte Anmerkungen.
In der strukturellen Beziehung zwischen Tabellen werden die Seiten- und Schnittstellentabellen letztendlich mit der Berechtigungstabelle verknüpft (Einzelheiten finden Sie unter Bitte lesen Sie meinen vorherigen Artikel „Verschiedenes über Berechtigungsdesign“.
Wir hoffen nun, es durch eine andere Lösung ersetzen zu können, um niedrige Kosten zu erzielen und gleichzeitig ein gewisses Maß an Berechtigungskontrolle zu berücksichtigen. Hier stellen wir zwei Konzepte vor. Geschäftsmodul, Vorgangstyp.
Geschäftsmodul
Konzept: Abstraktion des Geschäftsmoduls im System in eine Art Daten, Wir können es beispielsweise in Form einer Zeichenfolge ausdrücken: Rollenverwaltung entspricht Rollenverwaltung, Benutzerverwaltung entspricht Benutzerverwaltung usw. Wir teilen die im System vorhandenen Geschäftsmodule nach dem „Prinzip der geringsten Privilegien“ auf und bilden schließlich einen Stapel verteilbarer Daten.
Nutzungsprinzipien: Die API-Schnittstelle, die Seite und die Funktion hängen im Wesentlichen logisch mit dem Geschäftsmodul zusammen. Daher können wir die API-Schnittstelle und die Seite ( und) vergleichen Funktionspunkte), um einen logischen Abgleich durchzuführen und die Beziehung zwischen der Seite und der Schnittstelle zu bestimmen.
Operationstyp
Konzept: Zusammenfassung aller Operationstypen im System in einem Wir kann diese Art von Daten auch in Form von Zeichenfolgen ausdrücken, zum Beispiel: add entspricht einer neuen Hinzufügung, allot entspricht einer Zuordnung usw. Wir unterteilen alle Betriebstypen im System durch „Datenlizenzen“ nach Geschäftsmodulen und bilden schließlich einen Stapel verteilbarer Daten.
Nutzungsprinzipien: Die Seite ist die Anzeige, der Funktionspunkt ist die Aktion und die Schnittstelle ist die Ressourcenbereitstellung für die endgültige Aktion aufgerufen werden, werden durch das „Geschäftsmodul“ bestimmt, der „Operationstyp“ bestimmt, wie die Ressource genutzt wird. Durch beides können Sie grob und genau feststellen, ob die durch den Funktionspunkt der Seite ausgelöste Schnittstelle innerhalb der Authentifizierung liegt.
Was ist nun ihre endgültige tatsächliche Verwendung, nachdem wir diese beiden Konzepte vorgeschlagen haben? Denken wir zunächst aus den folgenden Perspektiven darüber nach.
Sind die Daten in der Seitentabelle in der Datenbank oder der API-Schnittstellentabelle echt und gültig?
Die tatsächliche Nutzung der Seite oder Schnittstelle basiert auf der Existenz von Funktionen oder der Existenz von Daten in der Datenbanktabelle.
Ist in der Berechtigungsstruktur die einzige Möglichkeit zum Speichern von „Kontrollobjekten“ die Datenbank?
Schauen wir uns diese Probleme zunächst einmal an: Die Speicherung von „Kontrollobjekten“ kann in der Datenbank, im Code oder in der Konfigurationsdatei erfolgen Es muss nicht unbedingt in der Datenbank vorhanden sein. Um die zweite Frage zu beantworten: Wenn die Schnittstelleninformationen in der Datenbank vorhanden sind, der Server diese Schnittstelle jedoch nicht entwickelt hat, liegt ein Problem mit den Datenbankinformationen selbst oder der neuen Schnittstelle in der Datenbank vor Die bereitgestellte Schnittstelle muss auf der Serverseite wirksam werden. Dann gibt es die erste Frage: Die Daten in der Tabelle „Kontrollobjekt“ in der Datenbank sind nicht unbedingt wahr und gültig. So können wir die folgende Lösung finden:
Wir können das Anmerkungsformular verwenden, um die Dateninformationen von „Geschäftsmodul“ und „Vorgangstyp“ zu ergänzen Schnittstelle, Beide Arten von Informationen können in konstanten Klassen gespeichert werden
Fügen Sie beim Hinzufügen und Erstellen der Seitentabellenstruktur und der Seitenfunktionstabellenstruktur in der Datenbank das „Geschäftsmodul“ und „ hinzu. Felder „Operationstyp“ .
Sie können die Informationen zu „Geschäftsmodul“ und „Vorgangstyp“ in der Wörterbuchtabelle der Datenbank speichern.
Das Hinzufügen von Modulen oder Vorgängen führt zwangsläufig zum Hinzufügen von Schnittstellen, was zu einer Systembereitstellung führt. Diese Betriebs- und Wartungskosten können nicht reduziert werden Tabellenstruktur.
Diese Lösung eignet sich jedoch nur für nicht starke Steuerungsschnittstellenprojekte. Bei Projekten mit starker Steuerungsschnittstelle müssen die Seite und die Schnittstelle dennoch gebunden werden, obwohl dies enorme Betriebs- und Wartungskosten mit sich bringt. Darüber hinaus kann es auch nach Schnittstellen-Routing-Regeln unterteilt werden, z. B. /api/page/xxxx/ (wird nur für Seiten verwendet), /api/mobile/xxxxx (wird nur für mobile Endgeräte verwendet), um nur verwendete Schnittstellen zu klassifizieren nach Seiten Die Klassenschnittstelle führt nur eine Authentifizierung, aber keine Autorisierung durch, was ebenfalls den Zweck erreichen kann.
Nachdem eine theoretische Idee genehmigt wurde, wird der Rest in die technische Praxis umgesetzt. Wir verwenden das Sicherheitsframework von Apache Shiro, angewendet in der Spring Boot-Umgebung. Erklären Sie kurz die folgenden Anmerkungen von Shiro.
注解名 | 作用 |
---|---|
@RequiresAuthentication | 作用于的类、方法、实例上。调用时,当前的subject是必须经过了认证的。 |
@RequiresGuest | 作用于的类、方法、实例上。调用时,subject可以是guest状态。 |
@RequiresPermissions | 作用于的类、方法、实例上。调用时,需要判断suject中是否包含当前接口中的Permission(权限信息)。 |
@RequiresRoles | 作用于的类、方法、实例上。调用时,需要判断subject中是否包含当前接口中的Role(角色信息)。 |
@RequiresUser | 作用于的类、方法、实例上。调用时,需要判断subject中是否当前应用中的用户。 |
/** * 1.当前接口需要经过"认证"过程 * @return */ @RequestMapping(value = "/info",method = RequestMethod.GET) @RequiresAuthentication public String test(){ return "恭喜你,拿到了参数信息"; } /** * 2.1.当前接口需要经过权限校验(需包含 角色的查询 或 菜单的查询) * @return */ @RequestMapping(value = "/info",method = RequestMethod.GET) @RequiresPermissions(value={"role:search","menu":"search"},logical=Logical.OR) public String test(){ return "恭喜你,拿到了参数信息"; } /** * 2.2.当前接口需要经过权限校验(需包含 角色的查询 与 菜单的查询) * @return */ @RequestMapping(value = "/info",method = RequestMethod.GET) @RequiresPermissions(value={"role:search","menu":"search"},logical=Logical.OR) public String test(){ return "恭喜你,拿到了参数信息"; } /** * 3.1.当前接口需要经过角色校验(需包含admin的角色) * @return */ @RequestMapping(value = "/info",method = RequestMethod.GET) @RequiresRoles(value={"admin"}) public String test(){ return "恭喜你,拿到了参数信息"; } /** * 3.2.当前接口需要经过角色与权限的校验(需包含admin的角色,以及角色的查询 或 菜单的查询) * @return */ @RequestMapping(value = "/info",method = RequestMethod.GET) @RequiresRoles(value={"admin"}) @RequiresPermissions(value={"role:search","menu":"search"},logical=Logical.OR) public String test(){ return "恭喜你,拿到了参数信息"; }
In unserem tatsächlichen Verwendungsprozess müssen wir tatsächlich nur @RequiresPermissions und @RequiresAuthentication verwenden. Am Ende des vorherigen Abschnitts haben wir das Geschäftsmodul übernommen Die Operation zum Entkoppeln der Beziehung zwischen der Seite und der API-Schnittstelle ist genau die gleiche wie die Methode von Apache Shiro. Wir versuchen jedoch, @RequiresRoles so oft wie möglich zu verwenden, da es zu viele Rollenkombinationen gibt und der Rollenname in der Schnittstelle nicht eindeutig dargestellt werden kann (es ist schwierig anzugeben, dass die Schnittstelle zu einer bestimmten Rolle gehört, aber Sie). Ich kann definitiv wissen, dass die Schnittstelle zu bestimmten Geschäftsmodulen gehört) Einige Vorgänge)
Lassen Sie uns nun den gesamten Vorgangsprozess überprüfen.
Aber nur diese 5 Anmerkungen in Shiro zu haben, reicht definitiv nicht aus. Im tatsächlichen Nutzungsprozess fügen wir je nach Bedarf unsere eigene, einzigartige Geschäftslogik zur Berechtigungsauthentifizierung hinzu. Der Einfachheit halber können wir benutzerdefinierte Anmerkungen verwenden. Diese Methode ist nicht nur auf Apache Shiro anwendbar, sondern auch auf viele andere Frameworks wie Hibernate Validator, SpringMVC, und wir können sogar ein Verifizierungssystem schreiben, um Berechtigungen in AOP zu überprüfen, was kein Problem darstellt. Daher haben benutzerdefinierte Anmerkungen ein breites Einsatzspektrum. Aber hier implementieren ich nur benutzerdefinierte Annotationen, die dafür geeignet sind, basierend auf Shiro.
Annotationsklasse definieren
/** * 用于认证的接口的注解,组合形式默认是“或”的关系 */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface Auth { /** * 业务模块 * @return */ String[] module(); /** * 操作类型 */ String[] action(); }
Annotationsverarbeitungsklasse definieren
/** * Auth注解的操作类 */ public class AuthHandler extends AuthorizingAnnotationHandler { public AuthHandler() { //写入注解 super(Auth.class); } @Override public void assertAuthorized(Annotation a) throws AuthorizationException { if (a instanceof Auth) { Auth annotation = (Auth) a; String[] module = annotation.module(); String[] action = annotation.action(); //1.获取当前主题 Subject subject = this.getSubject(); //2.验证是否包含当前接口的权限有一个通过则通过 boolean hasAtLeastOnePermission = false; for(String m:module){ for(String ac:action){ //使用hutool的字符串工具类 String permission = StrFormatter.format("{}:{}",m,ac); if(subject.isPermitted(permission)){ hasAtLeastOnePermission=true; break; } } } if(!hasAtLeastOnePermission){ throw new AuthorizationException("没有访问此接口的权限"); } } } }
Shiro-Interception-Verarbeitungsklasse definieren
/** * 拦截器 */ public class AuthMethodInterceptor extends AuthorizingAnnotationMethodInterceptor { public AuthMethodInterceptor() { super(new AuthHandler()); } public AuthMethodInterceptor(AnnotationResolver resolver) { super(new AuthHandler(), resolver); } @Override public void assertAuthorized(MethodInvocation mi) throws AuthorizationException { // 验证权限 try { ((AuthHandler) this.getHandler()).assertAuthorized(getAnnotation(mi)); } catch (AuthorizationException ae) { if (ae.getCause() == null) { ae.initCause(new AuthorizationException("当前的方法没有通过鉴权: " + mi.getMethod())); } throw ae; } } }
Shiros Aop-Aspektklasse definieren
/** * shiro的aop切面 */ public class AuthAopInterceptor extends AopAllianceAnnotationsAuthorizingMethodInterceptor { public AuthAopInterceptor() { super(); // 添加自定义的注解拦截器 this.methodInterceptors.add(new AuthMethodInterceptor(new SpringAnnotationResolver())); } }
Shiros benutzerdefinierte Annotations-Startup-Klasse definieren
/** * 启动自定义注解 */ public class ShiroAdvisor extends AuthorizationAttributeSourceAdvisor { public ShiroAdvisor() { // 这里可以添加多个 setAdvice(new AuthAopInterceptor()); } @SuppressWarnings({"unchecked"}) @Override public boolean matches(Method method, Class targetClass) { Method m = method; if (targetClass != null) { try { m = targetClass.getMethod(m.getName(), m.getParameterTypes()); return this.isFrameAnnotation(m); } catch (NoSuchMethodException ignored) { } } return super.matches(method, targetClass); } private boolean isFrameAnnotation(Method method) { return null != AnnotationUtils.findAnnotation(method, Auth.class); } }
Gesamtreihenfolge der Ideen: Annotationsklasse definieren (Variablen definieren, die vom Unternehmen verwendet werden können) - > Annotationsverarbeitungsklasse definieren (Geschäftslogikverarbeitung über Variablen in Annotationen durchführen) -> Annotations-Interceptor definieren -> Definieren Sie abschließend Shiros benutzerdefinierte Annotations-Aktivierungsklasse. Die Schreibideen für andere benutzerdefinierte Anmerkungen ähneln dieser. Verwandte Empfehlungen:Detaillierte Erläuterung der Importmethode der benutzerdefinierten Annotationszuordnung thinkPHP21 benutzerdefinierte Tag-BibliothekDetaillierte Informationen zu benutzerdefinierten Annotationen in Java EinführungDetaillierte Erläuterung der Shiro-Autorisierungsimplementierung
Das obige ist der detaillierte Inhalt vonErweiterung der benutzerdefinierten Anmerkungen basierend auf Shiro – ausführliche Erklärung mit Bildern und Text. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!