springboot整合shiro框架记录
shiro简介
-
Apache Shiro 是 Java 的一个安全(权限)框架。
-
Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。
-
Shiro 可以完成:认证、授权、加密、会话管理、与Web 集成、缓存等。
shiro的基本功能
-
Authentication:身份认证/登录,验证用户是不是拥有相应的身份
-
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能进行什么操作,如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限
-
Session Management:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境,也可以是Web 环境的
-
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储
-
Web Support:Web 支持,可以非常容易的集成到Web 环境
-
Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率
-
Concurrency:Shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去
-
Testing:提供测试支持
-
“Run As”:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问
-
Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了
其实说白了shiro最关键的部分只有三个 Subject、SecurityManager、Realm,其中subject代表当前交互的对象,SecurityManager安全管理器负责管理所有的subject,最后就是 Realm 了,Shiro从Realm 获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm 得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm 看成DataSource.
springboot集成shiro的一些记录
-
导入pom相关的依赖之后主要就是config文件中的两个配置
-
ShiroConfig中主要是三个函数 从下至上依次调用
@Configuration
public class ShiroConfig {
//shiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
// 设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
// 添加shiro的内置过滤器
/*
anon: 无需认证就可以访问
authc: 必须认证了才能访问
user: 必须拥有记住我功能才能用
perms: 拥有对某个资源的权限才能访问
role: 拥有某个角色权限
*/
//拦截
Map<String, String> filterMap = new LinkedHashMap<>();
//filterMap.put("/user/add","authc");
//filterMap.put("/user/update","authc");
//授权,正常情况下,没有授权会跳转到为授权页面
filterMap.put("/user/add","perms[user:add]");
filterMap.put("/user/update","perms[user:update]");
filterMap.put("/user/*","authc");
bean.setFilterChainDefinitionMap(filterMap);
//设置登录的请求 跳转到登录页面 没有权限就返回到登录上面
bean.setLoginUrl("/toLogin");
//为授权页面
bean.setUnauthorizedUrl("/noauto");
return bean;
}
//DefaultWebSecurityManager
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 关联userRealm 中间商 管理realm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建realm对象,需要自定义类
@Bean
public UserRealm userRealm() {
return new UserRealm();
}
// 整合ShiroDialect: 用来整合 Shiro thymeleaf
@Bean
public ShiroDialect getShiroDialect() {
return new ShiroDialect();
}
}
- 因为上面配置文件需要用到userRealm因此需要单独自定义UserRealm继承realm类
//自定义的UserRealm
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了=>授权doGetAuthorizationInfo");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//添加权限 用户添加
//info.addStringPermission("user:add");
//拿到当前登录的这个对象
// 认证做的事情得到的用户需要在授权时候使用,
// 需要在认证的时候最后return SimpleAuthenticationInfo(user,user.getPwd(),""); 注意是第一个参数
Subject subject = SecurityUtils.getSubject();
//可以通过认证得到的对象然后直接强制转换得到
User currentUser = (User)subject.getPrincipal();//拿到user对象
//设置当前用户的权限
info.addStringPermission(currentUser.getPerms());
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了=>认证doGetAuthorizationInfo");
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
// 虚拟用户
//String name = "root";
//String password = "123456";
//if (!userToken.getUsername().equals(name)) {
// return null;//抛出异常 UnknownAccountException
//}
// 真实数据库 用户名、密码, 数据中取
User user = userService.queryUserByName(userToken.getUsername());
if (user == null) {//没有这个人
return null;
}
//首页
//Subject currentSubject = SecurityUtils.getSubject();
//Session session = currentSubject.getSession();
//session.setAttribute("loginUser",user);
// 密码认证,shiro做 shiro进行密码的比对更加安全
return new SimpleAuthenticationInfo(user,user.getPwd(),"");
}
}
- 主要难以理解的就是controller登录过程中的login函数,其实login底层调用shiro配置中的方法执行了很多层
主要原理就是:在handle中调用login方法,会将账号密码封装入token传入securityManager,然后securityManager从缓存中获取从数据库中查询的账号密码一并封装入info,最后调用match方法传入token和info进行密码匹配,匹配成功则不会出现主动抛出的异常。