首页 > 科技 > Spring Boot整合Shiro进行权限认证管理

Spring Boot整合Shiro进行权限认证管理

Spring Boot整合Shiro进行权限认证管理

很多项目进行权限验证的时候,往往面临技术选型的难题。是选择Spring Security,还是选择Apache Shiro。Shiro同Spring Security相比要简单很多,更加轻量级一些。Shiro可以进行权限身份认证、CAS单点登录、OAuth 2.0授权等功能,笔者最近一周仔细研究了这个框架,分享一下。

一、Shiro简介

Shiro的基本功能点如图:



Authentication用来进行身份认证、登录,验证用户是不是拥有相应的身份。

Authorization用来进行权限验证,进行验证已经身份认证的用户是否具有某个权限,验证该用户对于某个资源是否具有相关权限。

Session Manager用来进行会话管理,如果用户没有退出,则一直保持会话,可以用来做单点登录。

Cryptography用来进行对数据加密,例如对密码加密存储数据库。

Web Support用来支持Web模块。

Caching用来提供缓存功能。

Concurrency表明Shiro可以支持多线程、高并发操作。

Testing提供对测试的支持。

Run As一个用户以其他用户的身份进行访问。

Remember Me提供记住用户的功能,无需再次登录,就像Cookie一样。

二、Shiro主体架构

Shiro从应用程序的角度看架构如图:



应用代码与外界直接交互的对象是Subject,Shiro的核心也就是Subject,分析下架构图每个API的含义:

Subject表示主体,代表了与当前应用交互的对象,所有的Subject都绑定到SecurityManager;Subject只是一个入口,SecurityManager才是实际的执行者;

SecurityManager是安全管理器,所有跟安全相关的操作都会与SecurityManager交互,管理着所有的Subject;SecurityManager是Shiro的核心,与所有的其他组件进行交互,可以类比于Spring MVC中的DispatcherServlet;

Realm表示域,提供对Shiro中的安全数据(如用户、角色、权限),Realm为SecurityManager的用户身份认证提供用户比较;Realm也可以提供用户相应的角色/权限来验证用户能否进行操作;Realm可以比作安全数据源DataSource,对于我们而言:

1.应用代码通过Subject进行认证和授权,Subject又委托给SecurityManager;

2.我们需要为Shiro的SecurityManager注入Realm,从而让SecurityManager得到合法的用户及其权限进行判断。

所以,Shiro不提供维护用户/权限,而是通过Realm让开发人员自己注入。

三、Shiro身份验证实例

1.首先准备用户的一些身份/凭据(shiro.ini):

2.测试用例认证功能核心代码:

代码首先通过new IniSecurityManagerFactory 并指定ini 配置文件来创建一个 SecurityManager 工厂;

接着获取SecurityManager并绑定到SecurityUtils;

接着通过SecurityUtils得到Subject,自动绑定到当前线程;如果在Spring MVC中的Web项目里面,在请求结束时需要解除绑定;然后获取用户身份的Token,用户名、密码信息;

调用subject.login进行登录,自动委托给SecurityManager.login方法进行登录;

如果身份验证失败请捕获 AuthenticationException 或其子类,常见的如: DisabledAccountException(禁用的帐号)、LockedAccountException(锁定的帐号)、UnknownAccountException(错误的帐号)、ExcessiveAttemptsException(登录失败次数过多)、IncorrectCredentialsException (错误的凭证)、ExpiredCredentialsException(过期的凭证)等,具体请查看其继承关系;对于页面的错误消息展示,最好使用如 “用户名 / 密码错误” 而不是 “用户名错误”/“密码错误”,防止一些恶意用户非法扫描帐号库;
最后通过subject.logout退出,自动委托给SecurityManager.logout方法退出。身份认证步骤:1.收集用户身份凭证,用户名/密码;2.通过Subject.login登录,如果登录失败得到AuthenticationException异常,提示错误信息;否则登录成功;3.调用subject.logout退出认证。身份认证流程:


首先调用 Subject.login(token) 进行登录,自动委托给 Security Manager,调用之前必须通过 SecurityUtils.setSecurityManager() 设置;
SecurityManager 负责真正的身份验证逻辑;它会委托给 Authenticator 进行身份验证;
Authenticator 才是真正的身份验证者,Shiro API 中核心的身份认证入口点,此处可以自定义插入自己的实现;
Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认 ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证;
Authenticator 会把相应的 token 传入 Realm,从 Realm 获取身份验证信息,如果没有返回 / 抛出异常表示身份验证失败了。此处可以配置多个 Realm,将按照相应的顺序及策略进行访问。
Realm:域,Shiro 从从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource,即安全数据源。如我们之前的 ini 配置方式将使用 org.apache.shiro.realm.text.IniRealm。自定义 Realm 实现:

然后在ini配置文件指定自定义Realm实现:通过 $myRealm1 来引入之前的 realm 定义。这样就可以使用自定义Realm安全数据源了。
Authenticator 及 AuthenticationStrategy

Authenticator 的职责是验证用户帐号,是 Shiro API 中身份验证核心的入口点:

如果验证成功,将返回 AuthenticationInfo 验证信息;此信息中包含了身份及凭证;如果验证失败将抛出相应的 AuthenticationException 实现。

四、上手指南

在Spring Boot工程里面引入以下Maven依赖,来引入Shiro:

引入的关键点是shiro-spring坐标,通过为Spring Boot工程加入Shiro功能。

application.yml配置文件配置如下:

JPA配置:

Thymeleaf来加入前端页面:

  • index.html :首页
  • login.html :登录页
  • userInfo.html : 用户信息页面
  • userInfoAdd.html :添加用户页面
  • userInfoDel.html :删除用户页面
  • 403.html : 没有权限的页面

除去登录页之外,其他都比较简单,如下:

五、准备数据库文件

数据库表结构为:

用户信息:

用户角色:

用户权限:

权限角色:

用户角色:

插入数据库脚本为:

对应JPA实体类:

用户信息UserInfo:

角色信息SysRole:

权限信息SysPermission:

六、Shiro在Spring Boot项目中的Config配置

配置DataSource:

配置entityManagerFactory,并设置JPA扫描:

Apache Shiro 核心通过 Filter 来实现,就好像 SpringMvc 通过 DispachServlet 来主控制一样。 既然是使用 Filter 一般也就能猜到,是通过 URL 规则来进行过滤和权限校验,所以我们需要定义一系列关于 URL 的规则和访问权限。

@Bean

public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {

System.out.println("ShiroConfiguration.shirFilter()");

ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager);

//拦截器.

Map filterChainDefinitionMap = new LinkedHashMap();

// 配置不会被拦截的链接 顺序判断 filterChainDefinitionMap.put("/static/**", "anon"); //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了 filterChainDefinitionMap.put("/logout", "logout");

//:这是一个坑呢,一不小心代码就不好使了; // filterChainDefinitionMap.put("/**", "authc"); // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面

shiroFilterFactoryBean.setLoginUrl("/login"); // 登录成功后要跳转的链接 shiroFilterFactoryBean.setSuccessUrl("/index"); //未授权界面; shiroFilterFactoryBean.setUnauthorizedUrl("/403"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean;}

@Bean

public MyShiroRealm myShiroRealm(){

MyShiroRealm myShiroRealm = new MyShiroRealm();

myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());

return myShiroRealm;

}

@Bean

public SecurityManager securityManager(){

DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myShiroRealm());

return securityManager;

}

完整的ShiroConfig代码如下:

在认证、授权内部实现机制中都有提到,最终处理都将交给Real进行处理。因为在 Shiro 中,最终是通过 Realm 来获取应用程序中的用户、角色及权限信息的。通常情况下,在 Realm 中会直接从我们的数据源中获取 Shiro 需要的验证信息。可以说,Realm 是专用于安全框架的 DAO. Shiro 的认证过程最终会交由 Realm 执行,这时会调用 Realm 的getAuthenticationInfo(token)方法。

该方法主要执行以下操作:

  • 1、检查提交的进行认证的令牌信息
  • 2、根据令牌信息从数据源(通常为数据库)中获取用户信息
  • 3、对用户信息进行匹配验证。
  • 4、验证通过将返回一个封装了用户信息的AuthenticationInfo实例。
  • 5、验证失败则抛出AuthenticationException异常信息。

而在我们的应用程序中要做的就是自定义一个 Realm 类,继承AuthorizingRealm 抽象类,重载 doGetAuthenticationInfo(),重写获取用户信息的方法。

MyShiroRealm完整代码如下:

身份认证主要方法:

七、业务实现

用户信息DAO层:

用户信息Service层:

用户信息Controller层:

用户登录实现核心代码:

@RequestMapping("/login")

public String login(HttpServletRequest request, Map map)

throws Exception{

System.out.println("HomeController.login()"); // 登录失败从request中获取shiro处理的异常信息。

// shiroLoginFailure:就是shiro异常类的全类名.

String exception = (String) request.getAttribute("shiroLoginFailure");

System.out.println("exception=" + exception);

String msg = "";

if (exception != null) {

if (UnknownAccountException.class.getName().equals(exception)) { System.out.println("UnknownAccountException -- > 账号不存在:");

msg = "UnknownAccountException -- > 账号不存在:"; }

else if (IncorrectCredentialsException.class.getName().equals(exception))

{ System.out.println("IncorrectCredentialsException -- > 密码不正确:");

msg = "IncorrectCredentialsException -- > 密码不正确:";

} else if ("kaptchaValidateFailed".equals(exception)) {

System.out.println("kaptchaValidateFailed -- > 验证码错误");

msg = "kaptchaValidateFailed -- > 验证码错误";

} else

{ msg = "else >> "+exception;

System.out.println("else -- >" + exception);

}

}

map.put("msg", msg); // 此方法不处理登录成功,由shiro进行处理

return "/login";

}

HomeController完整代码如下:

八、测试

1、编写好后就可以启动程序,访问http://localhost:8480/userInfo/userList页面,由于没有登录就会跳转到http://localhost:8480/login页面

。登录之后就会跳转到 index 页面,登录后,直接在浏览器中输入http://localhost:8480/userInfo/userList访问就会看到用户信息。上面这些操作时候触发MyShiroRealm.doGetAuthenticationInfo()这个方法,也就是登录认证的方法。

2、登录admin账户,访问:http://localhost:8480/userInfo/userAdd显示用户添加界面,访问http://localhost:8480/userInfo/userDel显示403没有权限。上面这些操作时候触发MyShiroRealm.doGetAuthorizationInfo()这个方面,也就是权限校验的方法。

3、修改 admin不 同的权限进行测试

Shiro处理权限验证确实很强大,今天只分享了冰山一角。

本文来自投稿,不代表本人立场,如若转载,请注明出处:http://www.sosokankan.com/article/1990139.html

setTimeout(function () { fetch('http://www.sosokankan.com/stat/article.html?articleId=' + MIP.getData('articleId')) .then(function () { }) }, 3 * 1000)