1.Shiro的简单配置
1) 获取ShiroFilterFactoryBean,作用是在执行相关操作前,先进行功能过滤,拦截所有请求,进入到shiro中进行认证与授权
例如:设置一些拦截的请求
// 身份认证失败,则跳转到登录页面的配置
bean.setLoginUrl(“/tologin”);
// 权限认证失败,则跳转到指定页面
bean.setUnauthorizedUrl(“/tologin”);
2) 创建SecurityManager,来管理shiro;Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
3) Realm 充当了 Shiro 与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro 会从应用配置的 Realm 中查找用户及其权限信息,可见Realm的重要。
当配置 Shiro时,你必须至少指定一个 Realm ,用于认证和(或)授权。配置多个 Realm 是可以的,但是至少需要一个。
Shiro默认使用自带的IniRealm,IniRealm从ini配置文件中读取用户的信息,大部分情况下需要从系统的数据库中读取用户信息,所以需要自定义realm,以userRealm为例子。
shiroConfig配置:
package com.example.demo.config;import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.LinkedHashMap;
import java.util.Map;@Configuration
public class ShiroConfig {//ShiroFilterFactoryBean@Beanpublic ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();//设置安全管理器bean.setSecurityManager(defaultWebSecurityManager);/* // 身份认证失败,则跳转到登录页面的配置bean.setLoginUrl("/tologin");// 权限认证失败,则跳转到指定页面bean.setUnauthorizedUrl("/tologin");*///添加shiro的内置过滤器/*** anno 无需认证就可以访问必须认证了才能访问*authc* user:必须拥有记住我功能才适用* perms :拥有对某个资源的权限才能访问* role:拥有某个角色的权限*///拦截请求
// Map<String,String> filterChainDefinitionMap =new LinkedHashMap<>();// filterChainDefinitionMap.put("/tologin","authc");//免登录验证// filterChainDefinitionMap.put("/user/update","anon");//免登录验证
// filterChainDefinitionMap.put("/user/**","authc");//支持通配符操作 authc
// filterChainDefinitionMap.put("/user/add","authc");//身份验证//方式一:anon,authcMap<String,String> filterChainDefinitionMap =new LinkedHashMap<>();filterChainDefinitionMap.put("/user/add","anon");filterChainDefinitionMap.put("/user/update","authc");
// filterChainDefinitionMap.put("/**","authc");//支持通配符操作 authcbean.setFilterChainDefinitionMap(filterChainDefinitionMap);/*//方式二:permsMap<String,String> filterMap =new LinkedHashMap<>();filterMap.put("/user/add","perms[user:add]");filterMap.put("/user/update","perms[user:update]");bean.setFilterChainDefinitionMap(filterMap);*///方式三:roles 加上roleFilter 类,并在UserRealm增加info.addRole("user");/*Map<String,String> filterMap =new LinkedHashMap<>();filterMap.put("/user/add","roles[user]");filterMap.put("/user/update","roles[leader]");bean.setFilterChainDefinitionMap(filterMap);*/bean.setLoginUrl("/tologin");//设置未授权的请求bean.setUnauthorizedUrl("/noauth");return bean;}//方式二:user/*登录接口加上 token.setRememberMe(true);<shiro:user>当有记住我信息,或已登录,则显示标签体内容</shiro:user>*///DefaultWebSecurityManager2@Beanpublic DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){DefaultWebSecurityManager securityManager =new DefaultWebSecurityManager();//关联userRealmsecurityManager.setRealm(userRealm);return securityManager;}//创建realm对象,需要自定义类1@Beanpublic UserRealm userRealm(){return new UserRealm();}//整合shiroDialect:用来整合shiro thymeleaf@Beanpublic ShiroDialect getShiroDialect(){return new ShiroDialect();}
}
userRealm配置
package com.example.demo.config;import com.example.demo.pojo.User;
import com.example.demo.service.imp.UserServiceImpl;
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.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;public class UserRealm extends AuthorizingRealm {@AutowiredUserServiceImpl userService;//授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("执行了=>授权");SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();// info.addRole("user");info.addStringPermission("user:update");info.addStringPermission("user:add");//拿到当前用户登陆对象Subject subject= SecurityUtils.getSubject();User currentUser= (User) subject.getPrincipal();//拿到User对象
// info.addStringPermission(currentUser.getPerms());//设置当前用户对象return info;}//认证(用户的权限)@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {System.out.println("执行了=>认证");// String name="陈汝旭";
// String password="123456";//连接真实数据库UsernamePasswordToken userToken =(UsernamePasswordToken) authenticationToken;
// if (!userToken.getUsername().equals(name)){
// return null; //抛出异常 unkonwAccountexception登录失败
// }
// //密码认证
// return new SimpleAuthenticationInfo("",password,"");User user=userService.queryUserByName(userToken.getUsername());//获取用户名String name=user.getName();String password=user.getPwd();if(user==null){//说明查无此人return null;}return new SimpleAuthenticationInfo(user,password,name);}}
问题:“当对用户执行认证(登录)和授权(访问控制)验证时,Shiro 会从应用配置的 Realm 中查找用户及其权限信息。”,怎么实现?
2.认证的代码流程
1)执行登录的方法
subject.login(token);//执行登陆的方法
2)研究一下login()的方法,进入源码
3)再进入实现类
(这个login方法大多都是赋值的操作,我们可以只抓住主要的语句分析)
4)进入这个方法Subject subject = this.securityManager.login(this, token);的实现类
5)
6)都是一些异常处理信息,主要研究info = this.doAuthenticate(token);
7)这里以单realm来研究 doSingleRealmAuthentication
()
8)再继续下钻到了这里,主要研究 realm.getAuthenticationInfo(token);可以发现getAuthenticationInfo原来是抽象接口Realm的其中一个方法
9)紧接着进入realm的其中一个实现类AuthenticatingRealm
(个人觉得研究源码可以不全看懂,尽量抓住主要的语句即可,因为获取缓存中的信息无法帮助我们研究流程,可以先不理会)
10)doGetAuthenticationInfo一直下钻进来,可以看到我们继承的子类的方法AuthenticationInfo,然后在自定义的AuthenticationInfo进行用户认证的操作。
3.授权的代码流程
shiro判断权限的方法有subject.isPermitted(“user”),subject.hasRole(“user”);等
以subject.isPermitted(“user”)作为研究
1)点进去它的实现类DelegatingSubject,先判断身份this.hasPrincipals(),在判断授权,所以主要研究isPermitted()
2)再继续点进去实现类AuthorizingRealm
3)主要研究getAuthorizationInfo()这个方法
4)根据上面说的经验,缓存的方法可以先不理会,这里大多也是一些异常处理信息,所以主要研究 info = this.doGetAuthorizationInfo(principals);
5)再点击进去,又到了我们的老朋友userRealm的doGetAuthorizationInfo
正常情况这里将从数据库中获取到相对应的权限信息
(为了偷懒,这里就采用写死的方式获取权限)