Shiro安全框架整合SpringBoot
Shiro安全框架整合
Apache Shiro™是一个功能强大且易于使用的Java安全框架,它执行身份验证,授权,加密和会话管理。使用Shiro易于理解的API,您可以快速轻松地保护任何应用程序-从最小的移动应用程序到最大的Web和企业应用程序。
关于Shiro与SpringBoot应用的关联关系图如下
springBoot项目中加入依赖
在原有的springBoot项目中加入依赖,版本看官网,一般都可以.当然版本越新功能越多,我使用的是1.5.3版本的。
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.3</version>
</dependency>
导入依赖后,我们需要配置相关的Shiro基本设置
Shiro的配置类 一般放在配置文件夹下conifg名的文件下,为了规范配置文件单独放置配置文件取名未ShiroConfig.java。相关的配置代码我也放在下面提供参考
import java.util.HashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
负责拦截所有的请求 创建 shiroFilter
@Bean
public ShiroFilterFactoryBean getShiroFilterBean(DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
//给filter 设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
//配置系统受限资源
Map<String,String> map =new HashMap<>();
map.put("/index","authc");
// <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
//配置系统公共资源
map.put("/login","anon");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/login");
// shiroFilterFactoryBean.setUnauthorizedUrl("/403");
return shiroFilterFactoryBean;
}
//创建安全管理器
//DefaultWebSecurityManager (第二步)
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm){
DefaultWebSecurityManager defaultWebSecurityManager=new DefaultWebSecurityManager();
//给安全管理器设置
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
}
//自定义realm
//创建Realm对象,需要自定义 (第一步)
@Bean
public Realm getRealm(){
UserRealm userRealm=new UserRealm();
return userRealm;
}
}
创建一个 UserRealm的类
并需要创建一个 UserRealm的类 是一个实体类,继承AuthorizingRealm
自定义的realm实现 目的是讲授权或者认证的数据来源转为数据库的实现
public class UserRealm extends AuthorizingRealm {
//控制权限
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//控制登录
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
return null;
}
}
关于拦截器
拦截器在使用中很常用,可以过滤未登录的用户,未授权的页面不可访问,那些公共资源放行,关于过滤的条件代码含义如图。可看图参考使用。
身份的认证
当完成这几步骤时 基础环境就配置好了下面需要进行身份的认证
身份认证需要在controller 里定义访问路径 则需要在拦截器中放行相关的路径操作内容如下
@RequestMapping("/loginAction")
public String loginAction(String username){
// UsernamePasswordToken token1=new UsernamePasswordToken(username,"");
//通常我们会将Subject对象理解为一个用户,同样的它也有可能是一个三方程序,它是一个抽象的概念,可以理解为任何与系统交互的“东西”都是Subject。
//通过SecurityUtils.getSubject()可以获得当前的Subject
Subject subject=SecurityUtils.getSubject();
//创建一个用户
AuthenticationToken token= new UsernamePasswordToken(username,"");
try {
//登录用户
subject.login(token);
return "loginPage";
}catch (Exception e){
return "登录失败";
}
};
对于需要加密密码的传输认证如下
通常我们会将Subject对象理解为一个用户,同样的它也有可能是一个三方程序,它是一个抽象的概念,可以理解为任何与系统交互的“东西”都是Subject。
public void testSubject() {
//1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
Factory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//2、得到SecurityManager实例 并绑定给SecurityUtils
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
//获得当前正在执行的subject
Subject subject = SecurityUtils.getSubject();
//获得shiro sessino实例
Session session = subject.getSession();
//创建一个用户
UsernamePasswordToken token = new UsernamePasswordToken("root","123");
//是否记住用户
token.setRememberMe(true);
//登录
try {
subject.login(token);
//没有抛异常则登录成功
String currentUser = subject.getPrincipal().toString();
System.out.println("当前登录的用户是:"+currentUser);
//判断用户是否是拥有某种角色
boolean isRole = subject.hasRole( "admin" );
//是否拥有某种功能
boolean isPer = subject.isPermitted("xiaoming:run");
//退出登录
subject.logout();
} catch ( UnknownAccountException uae ) {
System.out.println("用户名不存在");
} catch ( IncorrectCredentialsException ice ) {
System.out.println("密码错误");
} catch ( LockedAccountException lae ) {
System.out.println("用户被锁定,不能登录");
} catch ( AuthenticationException ae ) {
System.out.println("严重的错误");
}
}
完成controller 的创建用户后逻辑结构后
到 UserRealm类中去做数据的对比认证
UsernamePasswordToken创建的对象在UserRealm中的方法会自动引用
//控制登录
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
AuthenticationInfo authenticationInfo; //构造一个对象
//从 UsernamePasswordToken 中取得username 的值
String username=(String)authenticationToken.getPrincipal();
//在 UserRealm 中配置 UserServie 进行查询
User user =userServie.selectUserByUsername(username);
if (user!=null){
//进行验证参数1是返回数据库中正确的用户名,参数2 返回数据库中正确的密码 参数3提供当前realm的名字 this.getName
authenticationInfo=new SimpleAuthenticationInfo(username,"","realmName");
return authenticationInfo;
}
return null;
}
在实现类中实现方法
@Override
public User selectUserByUsername(String username) {
Wrapper wrapper=new EntityWrapper<>();
wrapper.eq(User.USERNAME,username);
List selectList=userMapper.selectList(wrapper);
if (selectList.size()>0){
return selectList.get(0);// 取第一个 或者直接使用userMapper.selectOne()
}
return null;
}
完成后效果为 没有认证不能访问拦截的网址,认证后则可以访问
数据访问权限的管理
//配置系统受限资源
//添加shiro内置过滤器
/*
* anno: 无需认证即可访问
* authc: 必须认证才可访问
* user: 必须拥有记住我功能
* perms: 拥有对某个资源的权限才可访问
* role: 拥有某个角色权限
*/
Map<String,String> map =new HashMap<>();
map.put("/index","authc");
// <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
//配置系统公共资源
map.put("/login","anon");
//把规则添加进去
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/login");
// shiroFilterFactoryBean.setUnauthorizedUrl("/403");
授权方法
//权限管理
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo info =new SimpleAuthorizationInfo();
String username=(String)principalCollection.getPrimaryPrincipal();
//将当前的用户的角色和权限复制进来
User user=userServie.selectUserByUsername(username);
Integer roleid = user.getRoleid();
Role role =roleMapper.selectById(roleid);
info.addRole(role.getRolename());
//当前用户拿到role的ID去查 rolename 然后复制给info
Wrapper<Permission> wrapper=new EntityWrapper<>();
wrapper.eq("roleid",role.getId());
List<Permission> selectList=permissionMapper.selectList(wrapper);
List<String>perlist=new ArrayList<>();
selectList.forEach(per->{
perlist.add(per.getPername());
});
info.addStringPermissions(perlist);
return info;
}
测试时 认证可通过 数据权限不能正常使用 上面那一段不行可以试试下面一段原理差不多
//授权中获取认证方法传来的principal
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了 授权doGetAuthorizationInfo");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//info.addStringPermission("user:add"); 可以这样添加权限标识,但一般从数据库从取出
//获取登录对象
Subject subject = SecurityUtils.getSubject();
//这个是从认证return的第一个参数(principal)获取
User user = (User)subject.getPrincipal();
info.addStringPermission(user.getPerms()); //设置从数据库获取到的权限
return info;
}