2021SC@SDUSC
基于web应用的登录认证和授权管理问题
据了解(加上个人理解),不使用安全框架,一般情况下,处理安全问题的数据库模型是RBAC(role base access controller),就是由角色控制可以访问的资源,然后给用户分配角色,这样就使得角色有了某种权限,权限就是各种controller的请求路径。这种做法就需要把所有的请求路径都管理起来,如果controller层的请求路径有变动,数据库就要变动,使得管理不方便。如果用面向对象的开放封闭原则来说,这算是一种差的做法。
然后再说一下shiro框架,就用了新的RBAC的思想,resources base access controller,基于资源的控制,把资源分类,然后管理起来,这样就又出现了粗粒度和细粒度权限这个区别。比如一个product类,从资源的角度看,粗粒度的权限就是能查看所有product,就是product:list,细粒度的权限就是能对某一id的product查看详情,product:show:[id]。
然后再加上方法级的权限控制和前端菜单显示控制,就能达到天衣无缝的权限控制。
springSecurity框架
Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.
Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements
它是一个强大的高度客制化的认证和访问控制的框架,它符合spring应用的标准。
它是一个聚焦于Java应用的认证和授权的框架,像所有的spring项目一样,它致力于能够轻松的满足客制化的需求。
使用框架的前提还是要很好的配置,感觉配置的好才能很好的发挥框架的作用。之前用过,是基于springboot的应用,但现在忘完了,一般是它配合Spring Security使用,所以只在这里提一下。
shiro框架
shiro框架是今天的主角。它同样非常强大。
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序百度。
主要功能
三个核心组件:Subject, SecurityManager 和 Realms.
Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。
Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。
Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果系统默认的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。
shiro框架具体使用
<artifactId>shiro-test</artifactId>
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.7.1</version>
</dependency>
</dependencies>
package com.shiro.test;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;
public class AuthenticationTest {
SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();
@Before
public void addUser(){
simpleAccountRealm.addAccount("Mark","123456","admin","user");
simpleAccountRealm.addAccount("Sam","123456","admin");
simpleAccountRealm.addAccount("Adam","123456","user");
}
public void testAuthentication(UsernamePasswordToken token){
//1.构建securityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(simpleAccountRealm);
//2.主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
System.out.println(token.getUsername()+" isAuthenticated:"+subject.isAuthenticated());
subject.checkRole("admin");
subject.logout();
System.out.println(token.getUsername()+" isAuthenticated:"+subject.isAuthenticated());
}catch (org.apache.shiro.authc.UnknownAccountException e){
System.out.println("用户名"+token.getUsername()+"不存在");
}catch (org.apache.shiro.authc.IncorrectCredentialsException e){
System.out.println("密码错误");
}catch (org.apache.shiro.authz.UnauthorizedException e){
System.out.println("用户权限不足");
}
}
@Test
public void testAuthentication(){
UsernamePasswordToken token1 = new UsernamePasswordToken("Mark","123456");
UsernamePasswordToken token2 = new UsernamePasswordToken("San","123456");
UsernamePasswordToken token3 = new UsernamePasswordToken("Adam","123456");
testAuthentication(token1);
//用户名不存在,抛出异常
testAuthentication(token2);
//用户密码错误,抛出异常
testAuthentication(token3);
}
}
自定义realm实现
自定义realm的实现,将决定如何实现登录认证和授权管理。重写的doGetAuthorizationInfo 和doGetAuthenticationInfo 方法被授权器和认证器调用。
先说一下doGetAuthenticationInfo 方法,用于认证的过程实现。大概流程就是根据获取的身份principal 从数据库查找该身份的密码credentials ,然后与在登录页面输入的密码进行对比,如果一样就通过认证,如果不一样就拒绝认证。
再说一下doGetAuthorizationInfo 方法,用于授权管理过程。在用户判断是否有某种权限时,调用此方法从数据库获取该principal 的权限,然后与要判断的权限做比较,返回布尔值。
认证具体流程
1、Shiro把用户的数据封装成标识token,token一般封装着用户名,密码等信息,用户登录时调用subject.login(token);
2、Subject把标识token交给SecurityManager,这里用的DefaultSecurityManager,
3、在SecurityManager安全中心中,SecurityManager把标识token委托给认证器AbstractAuthenticator,认证器获取到本次认证用的realm,
4、然后调用ModularRealmAuthenticator的doAuthenticate方法,它拿着自定义的realm和token去做认证。
授权具体流程
1、对subject进行授权,调用isPermitted(xxx),
2、securityManager执行授权,通过ModularRealmAuthorizer进行授权,
3、ModularRealmAuthorizer调用realm(自定义realm)的doGetAuthorizationInfo方法从数据库获取当前身份principal的权限返回给ModularRealmAuthorizer,
4、ModularRealmAuthorizer调用isPermitted进行权限串比较,返回Boolean值。
参考链接:https://blog.csdn.net/weixin_46119027/article/details/115754391