SpringBoot2.X Shiro用户权限管理业务笔记

Shiro主要用来进行权限管理。以前都是直接调用,没有对业务进行理解,当前我们自己重写了一遍Shiro的配置框架,现在记下来,以便学习。

一、概念

Shiro是一个安全框架,可以进行角色、权限管理。

Shiro主要功能如下:
Authentication(认证):用户身份识别,通常被称为用户“登录”
Authorization(授权):访问控制。比如某个用户是否具有某个操作的使用权限。
Session Management(会话管理):特定于用户的会话管理,甚至在非web 或 EJB 应用程序。
Cryptography(加密):在对数据源使用加密算法加密的同时,保证易于使用。


二、主要的类

1.Subject:当前用户,Subject可以是一个人,也可以是第三方服务
2.SecurityManager:管理所有Subject,可以配合内部安全组件。

3.principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/密码/手机号。
4.credentials:证明/凭证,即只有主体知道的安全值,如密码/数字证书等。
最常见的principals和credentials组合就是用户名/密码了。

5.Realms:用于进行权限信息的验证,需要自己实现。
6.Realm 本质上是一个特定的安全 DAO:它封装与数据源连接的细节,得到Shiro 所需的相关的数据。
在配置 Shiro 的时候,你必须指定至少一个Realm 来实现认证(authentication)和/或授权(authorization)。
我们需要实现Realms的Authentication 和 Authorization。其中 Authentication 是用来验证用户身份,Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。

 

使用方式:

第一步:首先我们需要将对应的依赖包给导入pom.xml当中


		
			org.apache.shiro
			shiro-spring
			1.3.2
		

第二步:我们需要创建一个Shiro的配置类,将需要拦截的访问请求拦截下来交给Shiro进行用户权限管理

package com.example.system.shiro;


import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Shiro配置方法类
 * @author Administrator
 */
@Configuration
public class ShiroConfig {
	
	/**
	 * Shiro业务过滤器配置方法
	 * @param securityManager
	 * @return
	 */
	@Bean(name = "shiroFilter")
	public ShiroFilterFactoryBean shiroFilter(org.apache.shiro.mgt.SecurityManager securityManager) {
			System.err.println("进入Shiro业务过滤配置");
			ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
			shiroFilterFactoryBean.setSecurityManager(securityManager);
	        shiroFilterFactoryBean.setLoginUrl("/login");
	        shiroFilterFactoryBean.setUnauthorizedUrl("/notRole");
	        Map filterChainDefinitionMap = new LinkedHashMap<>();
	        // 
	        filterChainDefinitionMap.put("/webjars/**", "anon");
	        filterChainDefinitionMap.put("/login", "anon");
	        filterChainDefinitionMap.put("/User/**", "anon");
	        filterChainDefinitionMap.put("/", "anon");
	        filterChainDefinitionMap.put("/front/**", "anon");
	        filterChainDefinitionMap.put("/api/**", "anon");
	        filterChainDefinitionMap.put("/admin/**", "authc");
	        filterChainDefinitionMap.put("/user/**", "authc");
	        //主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截 剩余的都需要认证
	        filterChainDefinitionMap.put("/**", "authc");
	        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
	        return shiroFilterFactoryBean;
	    }

		/**
		 * 安全控制器
		 * @return
		 */
	 	@Bean
	    public DefaultWebSecurityManager securityManager() {
	 		System.err.println("进入security安全控制器");
	        DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
	        defaultSecurityManager.setRealm(customRealm());
	        return defaultSecurityManager;
	        }
	 	
	 	/**
	 	 * 之定义域属性
	 	 * @return
	 	 */
	    @Bean
	    public CustomRealm customRealm() {
	        CustomRealm customRealm = new CustomRealm();
	        System.err.println("进入customRealm属性域控制器");
	        return customRealm;
	    }

}

第三步:我们需要继承Shiro的AuthorizingRealm(父类授权管理)的方法,对用户进行登陆管理和权限认证。

package com.example.system.shiro;


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.subject.PrincipalCollection;

import java.util.HashSet;
import java.util.Set;


/**
 * Shiro授权数据管理
 * @author Administrator
 *
 */
public class CustomRealm extends AuthorizingRealm {
	
	/**
	 * 	用户授权验证(目前没用到)
	 *	(源码注解内容大致为:从基础数据存储中检索给定主体的授权信息,pamar是主题信息主要标识的主题)
	 */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.err.println("用户权限验证!!!!!!!!!!");
    	String username = (String) SecurityUtils.getSubject().getPrincipal();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        Set stringSet = new HashSet<>();
        stringSet.add("user:show");
        stringSet.add("user:admin");
        info.setStringPermissions(stringSet);
        return info;
    }

    /**
     * 	获取即将需要认证的信息
     * 	这里可以注入userService进行数据库验证,为了方便演示,我这里写死了帐号了密码
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.err.println("--------身份认证方法- -------");
        String userName = (String) authenticationToken.getPrincipal();
        System.err.println("获取的用户名为:"+userName);
        String userPwd = new String((char[]) authenticationToken.getCredentials());
        //根据用户名从数据库获取密码
        String password = "12311";
        if (userName == null) {
            throw new AccountException("用户名不正确");
        } else if (!userPwd.equals(password )) {
            throw new AccountException("密码不正确");
        }
        return new SimpleAuthenticationInfo(userName, password,getName());
    }
}

第四步:我们在用户登陆业务接口中进行Shiro中的Subject.login调用即可。

	/**
	 * 用户登陆业务接口
	 * @return
	 */
	@RequestMapping(value = "login")
	@ResponseBody
	public String userLogin() {
		String username="小明123";
    	String password="12311";
    	Subject subject = SecurityUtils.getSubject();
        // 在认证提交前准备 token(令牌)
    	UsernamePasswordToken token= new UsernamePasswordToken(username, password);
    	// 执行认证登陆
        try {
            subject.login(token);
            Session session = subject.getSession();
            session.setTimeout(1200000L);//手动设置过期时间20分钟
        } catch (UnknownAccountException uae) {
            return "未知账户";
        } catch (IncorrectCredentialsException ice) {
            return "密码不正确";
        } catch (LockedAccountException lae) {
            return "账户已锁定";
        } catch (ExcessiveAttemptsException eae) {
            return "用户名或密码错误次数过多";
        } catch (AuthenticationException ae) {
            return "用户名或密码不正确!";
        }
        if (subject.isAuthenticated()) {
            return "登录成功";
        } else {
            token.clear();
            return "登录失败";
        }
	}

至此,目前登陆模块已经完结。Shiro的用户login会话会跳转到doGetAuthenticationInfo的方法当中对用户信息进行业务验证。

其封装方法会生成一个Sesssion和一个Cookie数据一个放到服务器一个返给前端进行保存。

每次前端进行业务访问的时候,(以下为我猜测部分,若不对,请好心提醒一下)Shiro内置方法会通过解析http消息内容中的Cookie数据来寻找服务器中有无对应的Session数据,若存在,则接口放行,更新Session有效期;若通过Cookie值没有获取到对应的Session值,则进行拦截。


这里开始补更新一下进行权限管理功能的方法和问题

在用户登陆时进行subject.login后,调用subject.isPermitted(权限名称)【看需求,也可以先判断权限再判断帐号密码】来写入用户的对应权限信息。

业务会进入doGetAuthorizationInfo方法中,对对用户的权限信息情况做查找,将用户信息数据写入Set当中,若用户某一个权限存在对应关系,则返true,否则返回False。

使用方法

可以在接口中调用 @RequiresRoles("权限名")标签,即可拦截非该权限用户访问,若费改权限用户访问即返回异常数据。

异常统一处理类

package com.example.system.shiro;

import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * 用户权限访问异常业务捕获
 * @author Administrator
 * @date 2019年6月29日 
 */
@ControllerAdvice
public class GlobalDefaultExceptionHandler {
	
	/**
	 * UnknownAccountException
	 * @param req
	 * @param e 
	 * @return
	 */
	@ExceptionHandler(UnauthorizedException.class)
    @ResponseBody
    public String defaultExceptionHandler(HttpServletRequest req, Exception e)
    {
        return "对不起,服务器繁忙...";
    }
}

 

你可能感兴趣的:(个人备忘,学习)