Shiro 是什么

Apache Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能:

  • 认证 - 用户身份识别,常被称为用户“登录”;
  • 授权 - 访问控制;
  • 密码加密 - 保护或隐藏数据防止被偷窥;
  • 会话管理 - 每用户相关的时间敏感的状态。

对于任何一个应用程序,Shiro都可以提供全面的安全管理服务。并且相对于其他安全框架,Shiro要简单的多。

Shiro的架构介绍

首先,来了解一下Shiro的三个核心组件:Subject, SecurityManager 和 Realms. 如下图: 
 
Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。 
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完整架构图: 
 
除前文所讲Subject、SecurityManager 、Realm三个核心组件外,Shiro主要组件还包括: 
Authenticator :认证就是核实用户身份的过程。这个过程的常见例子是大家都熟悉的“用户/密码”组合。多数用户在登录软件系统时,通常提供自己的用户名(当事人)和支持他们的密码(证书)。如果存储在系统中的密码(或密码表示)与用户提供的匹配,他们就被认为通过认证。 
Authorizer :授权实质上就是访问控制 - 控制用户能够访问应用中的哪些内容,比如资源、Web页面等等。 
SessionManager :在安全框架领域,Apache Shiro提供了一些独特的东西:可在任何应用或架构层一致地使用Session API。即,Shiro为任何应用提供了一个会话编程范式 - 从小型后台独立应用到大型集群Web应用。这意味着,那些希望使用会话的应用开发者,不必被迫使用Servlet或EJB容器了。或者,如果正在使用这些容器,开发者现在也可以选择使用在任何层统一一致的会话API,取代Servlet或EJB机制。 
CacheManager :对Shiro的其他组件提供缓存支持。

Shiro的使用

首先搭建一个springBoot项目

http://www.cnblogs.com/nbfujx/p/7694768.html

Maven Plugin添加Shiro相关jar包

  <!-- shiro   -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>${shiro.version}</version>
</dependency>

添加Shiro支持功能

 package com.goku.webapi.config.Shiro;

 import com.goku.webapi.mapper.ext.sysMenuExtMapper;
import com.goku.webapi.mapper.ext.sysUserExtMapper;
import com.goku.webapi.model.sysMenu;
import com.goku.webapi.model.sysRole;
import com.goku.webapi.model.sysUser;
import com.goku.webapi.service.sysUserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired; /**
* Created by nbfujx on 2017/11/7.
*/
public class ShiroRealm extends AuthorizingRealm { @Autowired
private sysUserExtMapper sysuserextmapper;
@Autowired
private sysMenuExtMapper sysmenuextmapper; /**
*权限验证
* **/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
sysUser user = sysuserextmapper.selectByUsername((String) principalCollection.getPrimaryPrincipal());
//把principals放session中 key=userId value=principals
SecurityUtils.getSubject().getSession().setAttribute(String.valueOf(user.getId()),SecurityUtils.getSubject().getPrincipals());
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//赋予角色
for(sysRole userRole:user.getSysrole()){
info.addRole(userRole.getRoleName());
}
//赋予权限
for(sysMenu menu:sysmenuextmapper.selectByUserId(user.getId())){
info.addStringPermission(menu.getPerms());
} return info; } /**
* 登录验证
* **/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String userName=token.getUsername();
sysUser user = sysuserextmapper.selectByUsername(token.getUsername());
if (user != null) {
//设置用户session
Session session = SecurityUtils.getSubject().getSession();
session.setAttribute("user", user);
return new SimpleAuthenticationInfo(userName,user.getPassword(),getName());
} else {
return null;
}
}
}

添加Shiro配置文件

 package com.goku.webapi.config.Shiro;

 import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.apache.shiro.mgt.SecurityManager;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn; import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map; /**
* shiro配置类
* Created by nbfujx on 2017/11/7.
*/
@Configuration
public class ShiroConfig {
/**
* LifecycleBeanPostProcessor,这是个DestructionAwareBeanPostProcessor的子类,
* 负责org.apache.shiro.util.Initializable类型bean的生命周期的,初始化和销毁。
* 主要是AuthorizingRealm类的子类,以及EhCacheManager类。
*/
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
} /**
* ShiroRealm,这是个自定义的认证类,继承自AuthorizingRealm,
* 负责用户的认证和权限的处理,可以参考JdbcRealm的实现。
*/
@Bean(name = "shiroRealm")
@DependsOn("lifecycleBeanPostProcessor")
public ShiroRealm shiroRealm() {
ShiroRealm realm = new ShiroRealm();
realm.setCacheManager(ehCacheManager());
return realm;
} /**
* EhCacheManager,缓存管理,用户登陆成功后,把用户信息和权限信息缓存起来,
* 然后每次用户请求时,放入用户的session中,如果不设置这个bean,每个请求都会查询一次数据库。
*/
@Bean(name = "ehCacheManager")
@DependsOn("lifecycleBeanPostProcessor")
public EhCacheManager ehCacheManager() {
return new EhCacheManager();
} /**
* SecurityManager,权限管理,这个类组合了登陆,登出,权限,session的处理,是个比较重要的类。
*/
@Bean(name = "securityManager")
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(shiroRealm());
securityManager.setCacheManager(ehCacheManager());
return securityManager;
} /**
* ShiroFilterFactoryBean,是个factorybean,为了生成ShiroFilter。
* 它主要保持了三项数据,securityManager,filters,filterChainDefinitionManager。
*/
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean() {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager()); Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
shiroFilterFactoryBean.setFilters(filters); Map<String, String> filterChainDefinitionManager = new LinkedHashMap<String, String>();
filterChainDefinitionManager.put("/login", "anon");
filterChainDefinitionManager.put("/logout", "anon");
filterChainDefinitionManager.put("/sysUser/*", "authc,perms");//"authc,perms[sysUser:*]");
filterChainDefinitionManager.put("/sysMenu/*", "authc,perms");//"authc,perms[sysUser:*]");
filterChainDefinitionManager.put("/*", "anon");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionManager); shiroFilterFactoryBean.setLoginUrl("/notAuthc");
shiroFilterFactoryBean.setSuccessUrl("/");
shiroFilterFactoryBean.setUnauthorizedUrl("/notAuthz");
return shiroFilterFactoryBean;
} /**
* DefaultAdvisorAutoProxyCreator,Spring的一个bean,由Advisor决定对哪些类的方法进行AOP代理。
*/
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
} /**
* AuthorizationAttributeSourceAdvisor,shiro里实现的Advisor类,
* 内部使用AopAllianceAnnotationsAuthorizingMethodInterceptor来拦截用以下注解的方法。
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor aASA = new AuthorizationAttributeSourceAdvisor();
aASA.setSecurityManager(securityManager());
return aASA;
} }

添加login验证类

 package com.goku.webapi.controller.impl;

 import com.alibaba.fastjson.JSON;
import com.goku.webapi.controller.loginController;
import com.goku.webapi.util.enums.returnCode;
import com.goku.webapi.util.message.returnMsg;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.session.SessionException;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ErrorAttributes;
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map; /**
* Created by nbfujx on 2017-11-07.
*/
@RestController
public class loginControllerImpl implements loginController,ErrorController { private final static String ERROR_PATH = "/error"; @Autowired
private ErrorAttributes errorAttributes; @RequestMapping(value = "/login", method = RequestMethod.GET)//测试方法 实际用post方法
public String login(
@RequestParam(value = "username", required = true) String userName,
@RequestParam(value = "password", required = true) String password,
@RequestParam(value = "rememberMe", required = true, defaultValue = "false") boolean rememberMe
) {
String passwordmd5 = new Md5Hash(password, "2").toString();
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(userName, passwordmd5);
token.setRememberMe(rememberMe);
try {
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
return JSON.toJSONString (new returnMsg(returnCode.ERROR));
}
return JSON.toJSONString (new returnMsg(returnCode.SUCCESS));
} @Override
@RequestMapping(value = "/logout", method = RequestMethod.GET)
public String logout() {
Subject subject = SecurityUtils.getSubject();
try {
subject.logout();
}catch (SessionException e){
e.printStackTrace();
return JSON.toJSONString (new returnMsg(returnCode.ERROR));
}
return JSON.toJSONString (new returnMsg(returnCode.SUCCESS));
} @Override
@RequestMapping(value = "/notAuthc", method = RequestMethod.GET)
public String notAuthc() {
return JSON.toJSONString (new returnMsg(returnCode.NOTAUTHC));
} @Override
@RequestMapping(value = "/notAuthz", method = RequestMethod.GET)
public String notAuthz() {
return JSON.toJSONString (new returnMsg(returnCode.NOTAUTHZ));
} @Override
@RequestMapping(value =ERROR_PATH)
public String error(HttpServletRequest request)
{
Map<String, Object> body = getErrorAttributes(request, getTraceParameter(request));
return JSON.toJSONString (new returnMsg(returnCode.ERROR,body));
} @Override
public String getErrorPath() {
return ERROR_PATH;
} private boolean getTraceParameter(HttpServletRequest request) {
String parameter = request.getParameter("trace");
if (parameter == null) {
return false;
}
return !"false".equals(parameter.toLowerCase());
} private Map<String, Object> getErrorAttributes(HttpServletRequest request,boolean includeStackTrace) {
RequestAttributes requestAttributes = new ServletRequestAttributes(request);
Map<String, Object> map = this.errorAttributes.getErrorAttributes(requestAttributes,includeStackTrace);
String URL = request.getRequestURL().toString();
map.put("URL", URL);
return map;
} }

添加业务类,业务方法增加RequiresPermissions权限注解

有权限

 @Override
@RequestMapping(value="getUser/{id}", method = RequestMethod.GET)
@RequiresPermissions(value={"sysUser:selectByid"})
public String selectByid(@PathVariable String id) {
this.logger.info("selectByid");
return JSON.toJSONString (new returnMsg(returnCode.SUCCESS,sysuserService.selectByid(id)));
}

无权限

 @Override
@RequestMapping(value="getMenu/{id}", method = RequestMethod.GET)
@RequiresPermissions(value={"sysMenu:selectByid"})
public String selectByid(@PathVariable long id) {
return JSON.toJSONString (new returnMsg(returnCode.SUCCESS,sysmenuService.selectByid(id)));
}

Shiro的使用验证

首先我们访问未登录时的资源

进行登录

我们访问登录后有权限的资源

我们访问登录后没有权限的资源

我们可以对每个方法增加权限控制

GITHUB

github : https://github.com/nbfujx/learn-java-demo/tree/master/Goku.WebService.Simple.Shiro

SpringBoot搭建基于Apache Shiro的权限管理功能的更多相关文章

  1. SpringBoot搭建基于Apache Shiro+Redis的分布式Session共享功能

    我们在上一遍文档中已经完成了Shiro验证功能.(http://www.cnblogs.com/nbfujx/p/7773789.html),在此基础上我们将完成分布式Session共享功能. Red ...

  2. 【shiro】(5)---基于Shiro的权限管理

    基于Shiro的权限管理项目搭建 前面写了四篇有关权限的文章,算是这篇文章的铺垫了.这篇文章采用 开发环境           JDK1.8          Eclipse          Mav ...

  3. SpringBoot&Shiro实现权限管理

    SpringBoot&Shiro实现权限管理 引言 相信大家前来看这篇文章的时候,是有SpringBoot和Shiro基础的,所以本文只介绍整合的步骤,如果哪里写的不好,恳请大家能指出错误,谢 ...

  4. Spring+Shiro搭建基于Redis的分布式权限系统(有实例)

    摘要: 简单介绍使用Spring+Shiro搭建基于Redis的分布式权限系统. 这篇主要介绍Shiro如何与redis结合搭建分布式权限系统,至于如何使用和配置Shiro就不多说了.完整实例下载地址 ...

  5. SpringBoot与Shiro整合权限管理实战

    SpringBoot与Shiro整合权限管理实战 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] *观看本文章需要有一定SpringBoot整合经验* Shiro框架简介 Apach ...

  6. spring-boot(八) springboot整合shiro-登录认证和权限管理

    学习文章:springboot(十四):springboot整合shiro-登录认证和权限管理 Apache Shiro What is Apache Shiro? Apache Shiro是一个功能 ...

  7. springboot(十四):springboot整合shiro-登录认证和权限管理(转)

    springboot(十四):springboot整合shiro-登录认证和权限管理 .embody{ padding:10px 10px 10px; margin:0 -20px; border-b ...

  8. Apache Shiro 开源权限框架

    在 Web 项目中应用 Apache Shiro 开源权限框架 Apache Shiro 是功能强大并且容易集成的开源权限框架,它能够完成认证.授权.加密.会话管理等功能.认证和授权为权限控制的核心, ...

  9. springBoot2.0 配置shiro实现权限管理

    一.前言 基于上一篇springBoot2.0 配置 mybatis+mybatisPlus+redis 这一篇加入shiro实现权限管理 二.shiro介绍 2.1 功能特点 Shiro 包含 10 ...

随机推荐

  1. lua基础学习(五)

    一.Lua 模块与包 模块类似于一个封装库,从 Lua 5.1 开始,Lua 加入了标准的模块管理机制,可以把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低 ...

  2. elementUI -->实现简单的购物车

    <template> <div class="bbb"> <el-checkbox :indeterminate="isIndetermin ...

  3. 【Sass】常用知识点总结

    如何编译Sass Partials Variables colors font stacks 全局变量 Mixins 全局mixin 推荐的mixin插件 Bourbon Extend/Inherit ...

  4. python的并发GIL 了解

    gil  又称 global interpreter lock (全局解释器锁) #python 中一个线程对应于c语言中的一个线程 #gil使得同一个时刻只有一个线程在一个cpu上执行字节码,无法将 ...

  5. java读取文件完整版

    public static void readFileByBytes(String fileName) { File file = new File(fileName); InputStream in ...

  6. C语言I博客作业12-学期总结

    一.我学到的内容 二.我的收获 1. https://www.cnblogs.com/1076022899-lj/p/11576442.html 收获:第一次接触到C语言和PTA,第一次学习了博客园和 ...

  7. CentOS7之SVN服务配置

    操作系统:CentOS Linux release 7.2.1511 (Core)    Subversion软件版本:subversion-1.7.14-10.el7.x86_64 1.首先检查sv ...

  8. 洛谷 P3258 松鼠的新家 题解

    题面 貌似这道题暴力加玄学优化就可以AC? 下面是正解: 1.树链剖分: 我们在u到v之间都放一个糖果,可以将松鼠它家u到v的糖果数都加1.每一次将a[i]到a[i+1] (a数组是访问顺序)的节点加 ...

  9. python 列表操作-切片

  10. MySql 缓冲池(buffer pool) 和 写缓存(change buffer) 转

    应用系统分层架构,为了加速数据访问,会把最常访问的数据,放在缓存(cache)里,避免每次都去访问数据库. 操作系统,会有缓冲池(buffer pool)机制,避免每次访问磁盘,以加速数据的访问. M ...