• SpringBoot搭建基于Apache Shiro的权限管理功能


    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包

     1  <!-- shiro   -->
     2         <dependency>
     3             <groupId>org.apache.shiro</groupId>
     4             <artifactId>shiro-core</artifactId>
     5             <version>${shiro.version}</version>
     6         </dependency>
     7         <dependency>
     8             <groupId>org.apache.shiro</groupId>
     9             <artifactId>shiro-web</artifactId>
    10             <version>${shiro.version}</version>
    11         </dependency>
    12         <dependency>
    13             <groupId>org.apache.shiro</groupId>
    14             <artifactId>shiro-spring</artifactId>
    15             <version>${shiro.version}</version>
    16         </dependency>
    17         <dependency>
    18             <groupId>org.apache.shiro</groupId>
    19             <artifactId>shiro-ehcache</artifactId>
    20             <version>${shiro.version}</version>
    21         </dependency>
    View Code

    添加Shiro支持功能

     1 package com.goku.webapi.config.Shiro;
     2 
     3 import com.goku.webapi.mapper.ext.sysMenuExtMapper;
     4 import com.goku.webapi.mapper.ext.sysUserExtMapper;
     5 import com.goku.webapi.model.sysMenu;
     6 import com.goku.webapi.model.sysRole;
     7 import com.goku.webapi.model.sysUser;
     8 import com.goku.webapi.service.sysUserService;
     9 import org.apache.shiro.SecurityUtils;
    10 import org.apache.shiro.authc.*;
    11 import org.apache.shiro.authz.AuthorizationInfo;
    12 import org.apache.shiro.authz.SimpleAuthorizationInfo;
    13 import org.apache.shiro.realm.AuthorizingRealm;
    14 import org.apache.shiro.session.Session;
    15 import org.apache.shiro.subject.PrincipalCollection;
    16 import org.springframework.beans.factory.annotation.Autowired;
    17 
    18 /**
    19  * Created by nbfujx on 2017/11/7.
    20  */
    21 public class ShiroRealm extends AuthorizingRealm {
    22 
    23     @Autowired
    24     private sysUserExtMapper sysuserextmapper;
    25     @Autowired
    26     private sysMenuExtMapper sysmenuextmapper;
    27 
    28     /**
    29      *权限验证
    30      * **/
    31     @Override
    32     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    33         sysUser user = sysuserextmapper.selectByUsername((String) principalCollection.getPrimaryPrincipal());
    34         //把principals放session中 key=userId value=principals
    35         SecurityUtils.getSubject().getSession().setAttribute(String.valueOf(user.getId()),SecurityUtils.getSubject().getPrincipals());
    36         SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    37         //赋予角色
    38         for(sysRole userRole:user.getSysrole()){
    39             info.addRole(userRole.getRoleName());
    40         }
    41         //赋予权限
    42         for(sysMenu menu:sysmenuextmapper.selectByUserId(user.getId())){
    43             info.addStringPermission(menu.getPerms());
    44         }
    45 
    46         return info;
    47 
    48     }
    49 
    50     /**
    51      * 登录验证
    52      * **/
    53     @Override
    54     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    55         UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
    56         String userName=token.getUsername();
    57         sysUser user = sysuserextmapper.selectByUsername(token.getUsername());
    58         if (user != null) {
    59             //设置用户session
    60             Session session = SecurityUtils.getSubject().getSession();
    61             session.setAttribute("user", user);
    62             return new SimpleAuthenticationInfo(userName,user.getPassword(),getName());
    63         } else {
    64             return null;
    65         }
    66     }
    67 }
    View Code

    添加Shiro配置文件

      1 package com.goku.webapi.config.Shiro;
      2 
      3 import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
      4 import org.apache.shiro.cache.ehcache.EhCacheManager;
      5 import org.apache.shiro.spring.LifecycleBeanPostProcessor;
      6 import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
      7 import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
      8 import org.apache.shiro.web.filter.authc.LogoutFilter;
      9 import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
     10 import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
     11 import org.springframework.beans.factory.annotation.Qualifier;
     12 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
     13 import org.springframework.context.annotation.Bean;
     14 import org.apache.shiro.mgt.SecurityManager;
     15 import org.springframework.context.annotation.Configuration;
     16 import org.springframework.context.annotation.DependsOn;
     17 
     18 import javax.servlet.Filter;
     19 import java.util.LinkedHashMap;
     20 import java.util.Map;
     21 
     22 
     23 /**
     24  * shiro配置类
     25  * Created by nbfujx on 2017/11/7.
     26  */
     27 @Configuration
     28 public class ShiroConfig {
     29     /**
     30      * LifecycleBeanPostProcessor,这是个DestructionAwareBeanPostProcessor的子类,
     31      * 负责org.apache.shiro.util.Initializable类型bean的生命周期的,初始化和销毁。
     32      * 主要是AuthorizingRealm类的子类,以及EhCacheManager类。
     33      */
     34     @Bean(name = "lifecycleBeanPostProcessor")
     35     public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
     36         return new LifecycleBeanPostProcessor();
     37     }
     38 
     39 
     40     /**
     41      * ShiroRealm,这是个自定义的认证类,继承自AuthorizingRealm,
     42      * 负责用户的认证和权限的处理,可以参考JdbcRealm的实现。
     43      */
     44     @Bean(name = "shiroRealm")
     45     @DependsOn("lifecycleBeanPostProcessor")
     46     public ShiroRealm shiroRealm() {
     47         ShiroRealm realm = new ShiroRealm();
     48         realm.setCacheManager(ehCacheManager());
     49         return realm;
     50     }
     51 
     52     /**
     53      * EhCacheManager,缓存管理,用户登陆成功后,把用户信息和权限信息缓存起来,
     54      * 然后每次用户请求时,放入用户的session中,如果不设置这个bean,每个请求都会查询一次数据库。
     55      */
     56     @Bean(name = "ehCacheManager")
     57     @DependsOn("lifecycleBeanPostProcessor")
     58     public EhCacheManager ehCacheManager() {
     59         return new EhCacheManager();
     60     }
     61 
     62     /**
     63      * SecurityManager,权限管理,这个类组合了登陆,登出,权限,session的处理,是个比较重要的类。
     64      */
     65     @Bean(name = "securityManager")
     66     public DefaultWebSecurityManager securityManager() {
     67         DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
     68         securityManager.setRealm(shiroRealm());
     69         securityManager.setCacheManager(ehCacheManager());
     70         return securityManager;
     71     }
     72 
     73 
     74     /**
     75      * ShiroFilterFactoryBean,是个factorybean,为了生成ShiroFilter。
     76      * 它主要保持了三项数据,securityManager,filters,filterChainDefinitionManager。
     77      */
     78     @Bean(name = "shiroFilter")
     79     public ShiroFilterFactoryBean shiroFilterFactoryBean() {
     80         ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
     81         shiroFilterFactoryBean.setSecurityManager(securityManager());
     82 
     83         Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
     84         shiroFilterFactoryBean.setFilters(filters);
     85 
     86 
     87         Map<String, String> filterChainDefinitionManager = new LinkedHashMap<String, String>();
     88         filterChainDefinitionManager.put("/login", "anon");
     89         filterChainDefinitionManager.put("/logout", "anon");
     90         filterChainDefinitionManager.put("/sysUser/*", "authc,perms");//"authc,perms[sysUser:*]");
     91         filterChainDefinitionManager.put("/sysMenu/*", "authc,perms");//"authc,perms[sysUser:*]");
     92         filterChainDefinitionManager.put("/*", "anon");
     93         shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionManager);
     94 
     95         shiroFilterFactoryBean.setLoginUrl("/notAuthc");
     96         shiroFilterFactoryBean.setSuccessUrl("/");
     97         shiroFilterFactoryBean.setUnauthorizedUrl("/notAuthz");
     98         return shiroFilterFactoryBean;
     99     }
    100 
    101     /**
    102      * DefaultAdvisorAutoProxyCreator,Spring的一个bean,由Advisor决定对哪些类的方法进行AOP代理。
    103      */
    104     @Bean
    105     @DependsOn("lifecycleBeanPostProcessor")
    106     public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
    107         DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
    108         defaultAAP.setProxyTargetClass(true);
    109         return defaultAAP;
    110     }
    111 
    112     /**
    113      * AuthorizationAttributeSourceAdvisor,shiro里实现的Advisor类,
    114      * 内部使用AopAllianceAnnotationsAuthorizingMethodInterceptor来拦截用以下注解的方法。
    115      */
    116     @Bean
    117     public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
    118         AuthorizationAttributeSourceAdvisor aASA = new AuthorizationAttributeSourceAdvisor();
    119         aASA.setSecurityManager(securityManager());
    120         return aASA;
    121     }
    122 
    123 
    124 
    125 
    126 }
    View Code

    添加login验证类

      1 package com.goku.webapi.controller.impl;
      2 
      3 import com.alibaba.fastjson.JSON;
      4 import com.goku.webapi.controller.loginController;
      5 import com.goku.webapi.util.enums.returnCode;
      6 import com.goku.webapi.util.message.returnMsg;
      7 import org.apache.shiro.SecurityUtils;
      8 import org.apache.shiro.authc.AuthenticationException;
      9 import org.apache.shiro.authc.UsernamePasswordToken;
     10 import org.apache.shiro.crypto.hash.Md5Hash;
     11 import org.apache.shiro.session.SessionException;
     12 import org.apache.shiro.subject.Subject;
     13 import org.springframework.beans.factory.annotation.Autowired;
     14 import org.springframework.boot.autoconfigure.web.ErrorAttributes;
     15 import org.springframework.boot.autoconfigure.web.ErrorController;
     16 import org.springframework.http.HttpStatus;
     17 import org.springframework.web.bind.annotation.RequestMapping;
     18 import org.springframework.web.bind.annotation.RequestMethod;
     19 import org.springframework.web.bind.annotation.RequestParam;
     20 import org.springframework.web.bind.annotation.RestController;
     21 import org.springframework.web.context.request.RequestAttributes;
     22 import org.springframework.web.context.request.ServletRequestAttributes;
     23 
     24 import javax.servlet.http.HttpServletRequest;
     25 import java.util.HashMap;
     26 import java.util.Map;
     27 
     28 
     29 /**
     30  * Created by nbfujx on 2017-11-07.
     31  */
     32 @RestController
     33 public class loginControllerImpl implements loginController,ErrorController {
     34 
     35 
     36     private final static String ERROR_PATH = "/error";
     37 
     38     @Autowired
     39     private ErrorAttributes errorAttributes;
     40 
     41     @RequestMapping(value = "/login", method = RequestMethod.GET)//测试方法 实际用post方法
     42     public String login(
     43             @RequestParam(value = "username", required = true) String userName,
     44             @RequestParam(value = "password", required = true) String password,
     45             @RequestParam(value = "rememberMe", required = true, defaultValue = "false") boolean rememberMe
     46     ) {
     47         String passwordmd5 = new Md5Hash(password, "2").toString();
     48         Subject subject = SecurityUtils.getSubject();
     49         UsernamePasswordToken token = new UsernamePasswordToken(userName, passwordmd5);
     50         token.setRememberMe(rememberMe);
     51         try {
     52             subject.login(token);
     53         } catch (AuthenticationException e) {
     54             e.printStackTrace();
     55             return JSON.toJSONString (new  returnMsg(returnCode.ERROR));
     56         }
     57         return  JSON.toJSONString (new  returnMsg(returnCode.SUCCESS));
     58     }
     59 
     60 
     61     @Override
     62     @RequestMapping(value = "/logout", method = RequestMethod.GET)
     63     public String logout() {
     64         Subject subject = SecurityUtils.getSubject();
     65         try {
     66             subject.logout();
     67         }catch (SessionException e){
     68             e.printStackTrace();
     69             return JSON.toJSONString (new  returnMsg(returnCode.ERROR));
     70         }
     71         return JSON.toJSONString (new  returnMsg(returnCode.SUCCESS));
     72     }
     73 
     74     @Override
     75     @RequestMapping(value = "/notAuthc", method = RequestMethod.GET)
     76     public String notAuthc() {
     77         return JSON.toJSONString (new  returnMsg(returnCode.NOTAUTHC));
     78     }
     79 
     80     @Override
     81     @RequestMapping(value = "/notAuthz", method = RequestMethod.GET)
     82     public String notAuthz() {
     83         return  JSON.toJSONString (new  returnMsg(returnCode.NOTAUTHZ));
     84     }
     85 
     86     @Override
     87     @RequestMapping(value =ERROR_PATH)
     88     public String error(HttpServletRequest request)
     89     {
     90         Map<String, Object> body = getErrorAttributes(request, getTraceParameter(request));
     91         return  JSON.toJSONString (new  returnMsg(returnCode.ERROR,body));
     92     }
     93 
     94     @Override
     95     public String getErrorPath() {
     96         return ERROR_PATH;
     97     }
     98 
     99     private boolean getTraceParameter(HttpServletRequest request) {
    100         String parameter = request.getParameter("trace");
    101         if (parameter == null) {
    102             return false;
    103         }
    104         return !"false".equals(parameter.toLowerCase());
    105     }
    106 
    107     private Map<String, Object> getErrorAttributes(HttpServletRequest request,boolean includeStackTrace) {
    108         RequestAttributes requestAttributes = new ServletRequestAttributes(request);
    109         Map<String, Object> map = this.errorAttributes.getErrorAttributes(requestAttributes,includeStackTrace);
    110         String URL = request.getRequestURL().toString();
    111         map.put("URL", URL);
    112         return map;
    113     }
    114 
    115 }
    View Code

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

    有权限

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

    无权限

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

    Shiro的使用验证

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

    进行登录

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

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

     

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

    GITHUB

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

  • 相关阅读:
    Windows 驱动加载程序代码
    coding push 上传文件
    MinGW 可以编译驱动的
    通过使用 NTLite 工具实现精简Windows系统
    一些常用的注入技巧
    Python 图片转字符图
    MySQL数据库及注入方法
    路由器被蹭网后,我有被黑的风险吗?
    markdown 实现代码折叠效果
    Windows 签名伪造工具的使用,Python,签名
  • 原文地址:https://www.cnblogs.com/nbfujx/p/7773789.html
Copyright © 2020-2023  润新知