SpringBoot 项目 Shiro 的实现

18 篇文章 0 订阅
15 篇文章 0 订阅

2019-04-30, 明天是五一劳动节,准备在4月结束前把 Token 令牌登录(SpringBoot 整合 JWT )的项目接口与将文件管理系统的接口对接,发现学长给的这个用户认证登录的接口有些问题(后续再说),没能顺利的进行。

  1. 在学习 SSM 框架的时候,有接触到 Shiro ,其中的登录是通过 Token 完成的。这样我便开始 SpringBoot 整合 Shiro。

一、Shiro 的简介

  1. Shiro 的官网 :http://shiro.apache.org/
  2. 将Apache Shiro集成到基于Spring的应用程序中

  Shiro 完整架构图
Shiro 结构图
   Shiro 是 Apache 下的一个开源项目(Apache Shiro),是一个易用与 Java 项目的安全框架,提供了 认证、授权、加密、会话管理,与 Spring Security 一样都是做了一个权限的安全框架。
  Shiro 核心的三大组件
在这里插入图片描述
  Shiro 的三大核心组件:
    1、Subject 当前用户。
    2、SecurityManage 管理所有的 Subject 。
    3、Reamls 权限信息的验证 。
   除了三大组件,还要认识的内容:
    * authenticator:认证器,主体进行认证最终通过authenticator进行的。
    * authorizer:授权器,主体进行授权最终通过authorizer进行的。
    * sessionManager:web应用中一般是用web容器对session进行管理,shiro也提供一套 - session管理的方式。
    * SessionDao: 通过SessionDao管理session数据,针对个性化的session数据存储需要使用sessionDao。
    * cache Manager:缓存管理器,主要对session和授权数据进行缓存,比如将授权数据通过cacheManager进行缓存管理,和ehcache整合对缓存数据进行管理。
    * realm:域,领域,相当于数据源,通过realm存取认证、授权相关数据。
    * cryptography:密码管理,提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。比如 md5散列算法。
  Shiro 的实现要点:
    1、实现 Reamls 的 Authentication(验证用户的身份) 与 Authorization(用户授权访问控制)。
    2、Shiro 是通过 Filter(过滤器)来实现的,就好比 SpringMVC 使用 DispatchServlet 来做控制,就是多个过滤器按照顺序执行。Shiro 是通过 URL 匹配规则来进行过滤和权限校验的,所以我们需要定义一系列的 URL 规则和访问权限。

Shiro 默认的拦截器拦截器描述用例过滤器的类 org.apache.shiro.web.filter
anon可匿名访问注册等可以匿名访问。/admin/register/**=anon.authc.AnonymousFilter
authc认证成功后才能使用(Authorization 执行成功)设置需要认证权限的界面。/user/**=authc.authc.FormAuthenticationFilter
logout注销登录的时候,任何 Session 都失效,任何身份都将失效用户退出。/logout=logout.authc.LogoutFilter
authcBasic表示需通过 HttpBasic验证/user/**=authBasic.authc.BasicHttpAuthenticationFilter
perms权限过滤器/admins/**=perms[user:add:*];/admins/user/**=perms[“user:add:*,user:modify:*”].authz.PermissionsAuthorizationFilter
port端口过滤器,可以设置是否是指定端口如果不是跳转到登录页面/admins/**=port[8081].authz.PortFilter
resthttp方法过滤器可以指定如post不能进行访问等,/admins/user/**=rest[user:method],其中method 为 post、get等.authz.HttpMethodPermissionFilter
roles角色过滤器,判断当前用户是否指定角色。当有多个参数时,每个参数通过才算通过,相当于 hasAllRoles() 方法/admins/**=roles[“admin,guest”].authz.RolesAuthorizationFilter
ssl请求需要通过ssl,如果不是跳转登录页暂无.authz.SslFilter
user如果访问一个已知用户,比如记住我功能,走这个过滤器暂无.authc.UserFilter
noSessionCreation阻止在请求期间创建新的会话。以保证无状态的体验暂无.session.NoSessionCreationFilter

    3、Shiro 通过提供的会话管理可以获取 Session 中的信息,同样可以使用 CacheManage 来管理。
    4、Shiro 的认证流程:
     (1)、在登录页面输入账号密码,传递给 Controller,在 Controller 中通过 Security.getSubject() 获取当前的 Subject 。
     (2)、通过 Subject 的 isAuthenticated() ,验证当前用户是否已经被认证。
     (3)、如果没有被认证,则开始认证。
     (4)、将从前台传来的账号密码封装到一个 UsernamePasswordToken 对象中。
     (5)、调用当前 Subject 的 login() 方法。这会把 token 作为参数传递到自定义的 Realm 的 doGetAuthenticationInfo()方法中。
     (6)、在 doGetAuthenticationInfo() 方法中,首先将 AuthenticationToken 转换为 UsernamePasswordToken 对象,然后调用 Service 层,根据 UsernamePasswordToken 中的用户名到数据库中去查询密码。
     (7)、由 Shiro 完成密码的比对,密码的比对是通过 AuthenticatingRealm 的 credentialsMatcher 属性来进行比对的。
    5、Shiro 的授权流程。
     (1)、构造 SecurityManager 环境后,对 subject 进行授权,调用方法 isPermitted(”permission串”)
     (2)、SecurityManager 执行授权,通过 ModularRealmAuthorizer 执行授权
     (3)、ModularRealmAuthorizer 执行 realm(自定义的CustomRealm)从数据库查询权限数据
调用realm的授权方法:doGetAuthorizationInfo()
     (4)、realm 从数据库查询权限数据,返回ModularRealmAuthorizer
     (5)、ModularRealmAuthorizer 调用 PermissionResolver 进行权限串比对
     (6)、如果比对后,isPermitted 中”permission串”在 realm 查询到权限数据中,说明用户访问 permission 串有权限,否则没有权限,抛出异常。

二、Shiro 的实现。

将Apache Shiro集成到基于Spring的应用程序中(SSM 实现)

在这里插入图片描述

独立应用程序

我是直接开发的 Web ,这里是官网标出来,我大概理解了一下。如有什么不对的地方,请见谅。
在这里插入图片描述

  • 自定义的 Realm 必须继承 AuthorizingRealm,并实现两个 Abstract 方法 doGetAuthorizationInfo 和 doGetAuthenticationInfo。

  • 页面的登录表单(Login Form)按照官方文档的写法,表单元素的名字都和文档一样(即:username、password 、rememberMe),表单登录(Login Action)只需要完成验证失败的跳转。验证成功变跳转至 successUrl。

默认的 FormAuthenticationFilter 会找这三个requestparameters:username、password 、rememberMe。

设置FormAuthenticationFilter的这几个参数。
	authc.loginUrl = /login.html 
	authc.usernameParam = adminName
	authc.passwordParam = adminPass
	authc.rememberMeParam = addCookie

2.1、SSM 框架配置文件的方式 集成 Apache Shiro 。

SSM 框架 Shiro 的实现,请参考:https://blog.csdn.net/Roobert_Chao/article/details/89971383

2.2、Spring Boot 集成 Apache Shiro 。

Spring Boot 集成 Apache Shiro,请参考: https://blog.csdn.net/Roobert_Chao/article/details/89971828
Spring Boot 集成 Apache Shiro 并添加 Cache ,请参考:https://blog.csdn.net/Roobert_Chao/article/details/90046565

三、Shiro 的方法总结。

3.1、Shiro 支持三种方式的授权。

  第一种,subject.hasRole(“admin”);主体中有admin权限?

Subject subject = SecurityUtils.getSubject();
if(subject.hasRole(“admin”)) {
	// 执行有权限的操作
} else {
	// 执行无权限的操作
}

  第二种,<shiro:hasRole name=’’ > JSP页面的标签?

# 刚开始接触 Web 的时候,我只会写 JSP 页面。
<shiro:hasRole name="admin">
<!-- 有权限 -->
</shiro:hasRole>

  第三种,学会使用注解之后进常用的。

# 注解式:通过在执行的Java方法上放置相应
@RequiresRoles("admin")
public void hello() {
	//  执行有权限的操作 
}

3.2、@RequiresRoles 和 @RequiresPermissions 注解不生效。

  1. 上边,刚刚说过授权的第三种方式使用注解,但是注解必须必须要使用相应的bean才能生效。
  2. 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证,配置以下两个bean(DefaultAdvisorAutoProxyCreatorAuthorizationAttributeSourceAdvisor)。
# Shiro 权限注解要生效,必须配置 Spring AOP 通过设置 Shiro 的 SecurityManager 进行权限验证。
=================  注解的方式加入Bean ======================
@Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);			// 开启代理
        return advisorAutoProxyCreator;
}

@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
    AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
    authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());		// 配置安全管理器。
    return authorizationAttributeSourceAdvisor;
}
=================  配置文件的方式加入 Bean  ==================
<!-- 开启Shiro Spring AOP权限注解的支持;<aop:config proxy-target-class="true">表示代理类。 -->
<aop:config proxy-target-class="true">
<!-- 生命周期 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

<!-- 注解方式出现异常 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">  
    <property name="exceptionMappings">  
        <props>  
            <prop key="org.apache.shiro.authz.UnauthorizedException">shiro-test/refuse</prop>
        </props>  
    </property>  
</bean>
<!-- 认证用户的注解管理 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" 
                      depends-on="lifecycleBeanPostProcessor">
                      <property name="proxyTargetClass" value="true"/>  
 <bean>
 <!-- 授权用户的注解管理 -->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
    		<property name="securityManager" ref="securityManager"/>
</bean>
### 拦截异常 。
@ExceptionHandler({UnauthorizedException.class})  
@ResponseStatus(HttpStatus.UNAUTHORIZED)  
public ModelAndView processUnauthenticatedException(NativeWebRequest request, UnauthorizedException e) {  
    ModelAndView mv = new ModelAndView();  
    mv.addObject("exception", e);  
    mv.setViewName("unauthorized");  
    return mv;  
}  

3.3、Shiro 的注解。

  1. 注解既可以用在 controller 中,也可以用在 service 中使用。
  2. 建议将shiro注解放在 controller 中,因为如果 service 层使用了spring的事物注解,那么 shiro 注解将无效。

@RequiresAuthentication
表示当前 Subject 已经通过 login 进行了身份验证;即 Subject.isAuthenticated() 返回true。

@RequiresUser
表示当前 Subject 已经身份验证或者通过记住我登录的。

@RequiresGuest
表示当前 Subject 没有身份验证或通过记住我登录过,即是游客身份。

@RequiresRoles(value={“admin”, “user”}, logical= Logical.AND)
@RequiresRoles(value={“admin”})
@RequiresRoles({“admin“})
表示当前 Subject 需要角色 admin 和 user。

@RequiresPermissions (value={“user:create”, “user:delete”}, logical= Logical.OR)
表示当前 Subject 需要权限 user:create 或 user:delete。

3.4、unauthorizedUrl 无效。

  • <property name=“unauthorizedUrl” value="/unauthorized.html"/>
  • shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");

我们在测试之前,发现都起作用,但是在 SSM 框架整合之后,抛出异常,并没有跳转到无权限页面。原因是因为 页面的跳转交给springMVC来控制,权限认证不通过, SpringMVC 抛出了异常。
【方法一:注册 Bean SimpleMappingExceptionResolver 设置异常对应的 url,url 需要在 Controller 中指定

@Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
     SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
     Properties properties = new Properties();

     /*未授权处理页*/
     properties.setProperty("org.apache.shiro.authz.UnauthorizedException", "/unauthor.html");
     /*身份没有验证*/
     properties.setProperty("org.apache.shiro.authz.UnauthenticatedException", "/login.html");
     
     resolver.setExceptionMappings(properties);
     return resolver;
 }
  • 亲测可以。
    在这里插入图片描述
    【方法二:自定义异常类 Reslover 捕捉异常,如果异常为无权限异常就手动就是转发到无权页面。
1、首先设置异常的捕获类,
public class MyExceptionResolver implements HandlerExceptionResolver{
	public ModelAndView resolveException(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception e) {
		//  如果是shiro无权操作,因为shiro 在操作auno 等一部分不进行转发至无权限url
		if(e instanceof UnauthorizedException){
			ModelAndView mv = new ModelAndView("unauthor");
			return mv;
		}
		e.printStackTrace();
		ModelAndView mv = new ModelAndView("unauthor");
		mv.addObject("exception", e.toString().replaceAll("\n", "<br/>"));
		return mv;
	}
}
2. 自定义异常处理
<bean id="exceptionResolver" class="cn.chao.formyself.resolver.MyExceptionResolver"></bean> 

3.5、AJAX 请求,提示无角色无权限。

在上面的异常处理中,我们将判断一个请求,是否拥有角色的权限,无没有权限的话,则跳转到了无权限的页面中,那么我们要使用 AJAX 请求,返回的 error:function() 中应该是一个没有权限的提示信息。

【方法一:仍然使用第一种抛出异常的 Bean 。】

@Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
     SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
     Properties properties = new Properties();

     /*未授权处理页*/
     properties.setProperty("org.apache.shiro.authz.UnauthorizedException", "/访问方法判断是否是 AJAX 处理");
     /*身份没有验证*/
     properties.setProperty("org.apache.shiro.authz.UnauthenticatedException", "/login.html");
     
     resolver.setExceptionMappings(properties);
     return resolver;
 }

【方法二:配置文件结合 Controller 层的代码判断】

配置异常的拦截器
<!-- shiro 为集成 springmvc 拦截异常 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
	<property name="exceptionMappings" >
		<props>
		<!-- 这里可以配置 N 多个 错误异常转发 -->
			<prop key="org.apache.shiro.authz.AuthorizationException">redirect:/401</prop>
	</property>
</bean>
异常拦截器将异常重定向到了 RequestMapping 401 的 controller 中
@RequestMapping("/401")
public String authorizationException(ModelMap modelMap,HttpServletRequest request){
	String requestType = request.getHeader("x-Requested-With");
	// AJAX 请求
	if(requestType != null && requestType.equals("XMLHttpRequest")){
		return "redirect:ajax401";			// 我在这里抛出的是权限的异常,判断为 AJAX 的请求方式,这里调用 RequestMapping("ajax401") 的方法,返回String 类型字符串,提示没有数据。
	}else{
		return "redirect:syn401";			// 方法体直接放回 无权限页面。
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值