SpringBoot2.X整合Shiro

Shiro说明

Shiro 是一个强大、简单易用的 Java 安全框架,主要用来更便捷的认证,授权,加密,会话管等等,可为任何应用提供安全保障。本篇主要来介绍 Shiro 的认证和授权功能。

1. Shiro 三大核心组件

Shiro 有三大核心的组件: Subject 、 SecurityManager 和 Realm 。它们之间的关系如下图所示:
SpringBoot2.X整合Shiro_第1张图片1. Subject:认证主体。它包含两个信息:PrincipalsCredentials
Principals:身份。可以是用户名,邮件,手机号码等等,用来标识一个登录主体身份;
Credentials:凭证。常见有密码,数字证书等等。
通俗来说,就是需要认证的东西,最常见的就是用户名密码了,比如用户在登录的时候,Shiro 需要去进行身份认证,就需要 Subject 认证主体。
2. SecurityManager:安全管理员。这是 Shiro 架构的核心,它就像 Shiro 内部所有原件的保护伞一样。在项目中一般都会配置 SecurityManager,主要是在 Subject 认证主体上面下功夫。
3. Realms:Realms 是一个域,它是连接 Shiro 和具体应用的桥梁,当需要与安全数据交互的时候,比如用户账户、访问控制等,Shiro 就会从一个或多个 Realms 中去查找。我们一般会自己定制Realm,下面详细说明。

2.SpringBoot 2.1集成Shiro过程

2.1引入依赖


	    
		
		    org.apache.shiro
		    shiro-spring
		    1.4.0
		

	

2.2建立数据库表

用户表( user ,这里默认一个用户一个角色):

id user_name pwd salt role_id
1 zhang_san 123 a1 1
3 li_si 789 a3 2

角色表( role ):

role_id role_code role_name
1 admin 管理员
2 user 普通用户

权限表( auth ):

auth_id auth_code auth_name
1 data:dele 删除数据
2 data:add 添加数据
3 data:get 查询数据
4 data:edit 修改数据

role_auth 关联表
(这里默认 角色1管理员有全部权限,普通用户有“获取数据”和“添加数据”的权限)

id role_id auth_id
1 1 1
2 1 2
3 1 3
4 1 4
5 2 2
6 2 3

2.3自定义 Realm

有了数据库表和数据之后,我们开始自定义 realm,自定义 realm 需要继承 AuthorizingRealm 类,因
为该类封装了很多方法,它也是一步步继承自 Realm 类的,继承了 AuthorizingRealm 类后,需要重写
两个方法:

doGetAuthenticationInfo() 方法:用来验证当前登录的用户,获取认证信息
doGetAuthorizationInfo() 方法:用来为当前登陆成功的用户授予权限和角色

具体实现如下,相关的解释在代码的注释中,以便了解:

package com.tantela.config.shiro;

import com.tantela.dao.sys.UserMapper;
import com.tantela.model.sys.User;
import com.tantela.model.sys.UserShiro;
import com.tantela.service.sys.UserService;
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 org.springframework.beans.factory.annotation.Autowired;

import java.util.*;

/**
 * @Author: ttllihao
 * @Description: Realm 授权与认证配置
 */
public class RealmConfig extends AuthorizingRealm {

    @Autowired
    private UserService userService;
    @Autowired
    private UserMapper userMapper;

    /**
     * 用户验证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upt = (UsernamePasswordToken) token;
        // 根据token获取用户名
        String username = upt.getUsername();
        // 往数据库中查询用户
        User user = userService.selectByUserName(username);
        if (user == null) {
            throw new UnknownAccountException();
        }
        Integer userId = user.getUid();
        // 把当前用户 得 角色信息和权限信息存入会话
        // 向数据库中查询该账户拥有的角色
        List<String> roleList = userMapper.loginRole(userId);
        List<String> authList = userMapper.loginAuth(userId);
        user.setRoleList(roleList);// user类中有属性 List roleList
        user.setAuthList(authList);// user类中有属性 List authList

        // 把当前用户信息以及用户角色、权限存到ShiroSession中
        SecurityUtils.getSubject().getSession().setAttribute(String.valueOf(userId),user);
        // 传入用户名和密码进行身份认证,并返回认证信息
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                user,
                //密码
                user.getPassword(),
                //ByteSource.Util.bytes(user.getCredentialsSalt()),// 盐
                //getName()
                ""
        );
        return authenticationInfo;

    }

    /**
     * 用户授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        //获取认证时候添加到SimpleAuthenticationInfo中的实例
        User user = (User) principals.getPrimaryPrincipal();
        Integer userId = user.getUid();

        // 将会话中得权限取出来。用作授权
        Object object = SecurityUtils.getSubject().getSession().getAttribute(String.valueOf(userId));
        User userShiro = (User)object;
        // 将 List 转化为 Set,并实现去重
        Set<String> roleSet = new HashSet<>(userShiro.getRoleList());
        Set<String> authSet = new HashSet<>(userShiro.getAuthList());

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setRoles(roleSet);
        authorizationInfo.setStringPermissions(authSet);
        return authorizationInfo;
    }


}

2.4账号和密码的验证

在上一步的认证和授权中,账号和密码是如何验证的呐?这里需要一个登录入口,告知用户输入的账号和密码。

    @ResponseBody
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public Map<String, Object> app(HttpServletRequest request, HttpSession session,
                                   String username, String password, String code) {
        Map<String, Object> map = new HashMap<>(8);
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            //下一步到Realm中认证
            subject.login(token);
            map.put("errMsg", "登陆成功");
            map.put("success", true);
            map.put("code", 0);

            session.setAttribute("loginIp", sessionIp);
            session.setAttribute("userName",username);
            } catch (UnknownAccountException e) {
                map.put("errMsg", MessageUtil.LOGIN_FAIL);
                map.put("success", false);
                map.put("code", 1);
            } catch (IncorrectCredentialsException e) {
                map.put("errMsg", "账号或密码不正确");
                map.put("success", false);
                map.put("code", 1);
            } catch (LockedAccountException e) {

            } catch (AuthenticationException e) {
        return map;
    }

2.5 Shiro 配置

ShiroConfig.java

@Configuration
public class ShiroConfig {
	
	@Bean(name="shiroFilterFactoryBean")
	public ShiroFilterFactoryBean shirFilter(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		// 设置安全管理器
		shiroFilterFactoryBean.setSecurityManager(securityManager);		
		// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
		shiroFilterFactoryBean.setLoginUrl("/login");
		//  登录成功后要跳转的链接
	    shiroFilterFactoryBean.setSuccessUrl("/index"); 
	    // 未授权时跳转的提示界面
	    shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");
	    // 这里需要LinkedHashMap 不能HashMap (坑点之一:会出现代码已经配置却依然无权限访问的问题)
		Map filterMap = new LinkedHashMap();
			filterMap.put("/css/**","anon");
			filterMap.put("/img/**","anon");
			filterMap.put("/js/**","anon");
			filterMap.put("/html/**","anon");
			filterMap.put("/login/index","authc");
			filterMap.put("/login/login","anon");
			filterMap.put("/logout","logout");//配置退出 过滤器,其中的具体的退出代码Shiro已经实现
			filterMap.put("/**","authc");//过滤链定义,从上向下顺序执行,一般将/**放在最为下边
		// 	如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/login/loginPage");
        //  登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/system/index");
        //未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/system/noAuth");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
	    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
	    return shiroFilterFactoryBean ;
			
		}

	 /**
     * 权限管理,配置主要是Realm的管理认证
     */
	@Bean(name="securityManager")
	public DefaultWebSecurityManager securityManager(@Qualifier("myShiroRealm")RealmConfig realm){
		DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
	    securityManager.setRealm(realm);    
	    return securityManager;
	 }
	
	 /**
     * 将自己的验证方式加入容器
     */
	@Bean(name="myShiroRealm")
	public RealmConfig myShiroRealm(){
	   RealmConfig myShiroRealm = new RealmConfig();
	   //myShiroRealm.setCredentialsMatcher(hCM);
	   return myShiroRealm;
	 }
	/**
     * 开启shiro aop注解支持.(使用代理方式;所以需要开启代码支持;)
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorization(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    
	//凭证匹配器(由于密码校验交给Shiro的SimpleAuthenticationInfo进行处理了)
	//@Bean(name="hashedCredentialsMatcher")
	public HashedCredentialsMatcher hashedCredentialsMatcher(){
	   HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
	   hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
	   hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5(md5(""));
	   return hashedCredentialsMatcher;
	}
}

说明:
认证过滤器:
anon:表示可以匿名使用。
authc:表示需要认证(登录)才能使用,没有参数
authcBasic:没有参数表示httpBasic认证
user:当登入操作时不做检查

授权过滤器
roles:参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/=roles[“admin,guest”],每个参数通过才算通过,相当于hasAllRoles()方法。
perms:参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/
=perms[“user:add:,user:modify:”],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。
rest:根据请求的方法,相当于/admins/user/*=perms[user:method] ,其中method为post,get,delete等。
port:当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?后面的参数。
ssl:表示安全的url请求,协议为https

你可能感兴趣的:(SpringBoot,学习笔记)