Shiro框架简介
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序
Apache Shiro 体系结构
1、 Authentication 认证 ---- 用户登录 2、 Authorization 授权 --- 用户具有哪些权限 3、 Cryptography 安全数据加密 4、 Session Management 会话管理 5、 Web Integration web系统集成 6、 Integrations 集成其它应用,spring、缓存框架
SpringBoot整合Shiro完成认证
分析Shiro的核心主体
Subject: 用户主体(把操作交给SecurityManager) SecurityManager:安全管理器(关联Realm) Realm:Shiro连接数据的桥梁
Maven中导入Shiro
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
自定义Realm
/**
* 自定义Realm
* @author dhcurry
*
*/
public class UserRealm extends AuthorizingRealm{
/**
* 执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
System.out.println("执行授权逻辑");
//假设数据库的用户名和密码
String name = "eric";
String password = "123456";
//编写shiro判断逻辑,判断用户名和密码
//1.判断用户名
UsernamePasswordToken token = (UsernamePasswordToken)arg0;
if(!token.getUsername().equals(name)){
//用户名不存在
return null; //shiro底层会抛出UnKnowAccountException
}
// 方法的前面其实就是一个Principals,存储认证成功得到的信息
return new SimpleAuthenticationInfo(user,password,getName());
}
/**
* 执行认证逻辑
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
System.out.println("执行认证逻辑");
return null;
}
}
编写Shiro配置类
/**
* Shiro的配置类
* @author dhcurry
*
*/
@Configuration
public class ShiroConfig {
/**
* 创建ShiroFilterFactoryBean
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
//添加Shiro内置过滤器
/**
* Shiro内置过滤器,可以实现权限相关的拦截器
* 常用的过滤器:
* anon: 无需认证(登录)可以访问
* authc: 必须认证才可以访问
* user: 如果使用rememberMe的功能可以直接访问
* perms: 该资源必须得到资源权限才可以访问
* role: 该资源必须得到角色权限才可以访问
*/
Map<String,String> filterMap = new LinkedHashMap<String,String>();
/*filterMap.put("/add", "authc");
filterMap.put("/update", "authc");*/
filterMap.put("/testThymeleaf", "anon");
filterMap.put("/*", "authc");
//修改调整的登录页面
shiroFilterFactoryBean.setLoginUrl("/toLogin");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}
/**
* 创建DefaultWebSecurityManager
*/
@Bean(name="securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联realm
securityManager.setRealm(userRealm);
return securityManager;
}
/**
* 创建Realm
*/
@Bean(name="userRealm")
public UserRealm getRealm(){
return new UserRealm();
}
}
编写Controller
/**
* 登录逻辑处理
*/
@RequestMapping("/login")
public String login(String name,String password,Model model){
/**
* 使用Shiro编写认证操作
*/
//1.获取Subject
Subject subject = SecurityUtils.getSubject();
//2.封装用户数据
UsernamePasswordToken token = new UsernamePasswordToken(name,password);
//3.执行登录方法
try {
subject.login(token);
//登录成功
//跳转到test.html
return "redirect:/testThymeleaf";
} catch (UnknownAccountException e) {
//e.printStackTrace();
//登录失败:用户名不存在
model.addAttribute("msg", "用户名不存在");
return "login";
}catch (IncorrectCredentialsException e) {
//e.printStackTrace();
//登录失败:密码错误
model.addAttribute("msg", "密码错误");
return "login";
}
}
SpringBoot整合Shiro完成授权
修改reaml的授权部分
这个部分的职责其实就是给已经认证的用户给一个授权
一般授权的方式有两种,
-
一种最直接,直接给用户相关操作权限——shiro中控制权限使用的是字符串,换句话说,给权限就是给用户一个字符串,比如说system:view这个字符串,而赋予用户这个字符串的任务就交给了自定义realm中的授权方法,相应的,我们在资源上设置一个需要某system:view的字符串注解——shiro中有相关注解:@RequiresPermissions(),如果用户要访问该资源,那么就必须持有相应的字符串,不然无法访问。
-
第二种为了管理方便,我们将一组权限赋予给某个角色,然后用户只要持有某个特定的角色就可以访问相应的资源,同样,赋予用户角色的任务就交给了realm,shiro中也有相应注解@RequiresRoles()
下面是示例代码:
public class UserRealm extends AuthorizingRealm {
@Autowired
private LoginService loginService;
// 授权模块
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermission("system:view");
info.addRole("admin");
return info;
}
// 认证模块
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken uptoken = (UsernamePasswordToken) authenticationToken;
String username = uptoken.getUsername();
String password = "";
if(uptoken.getPassword() != null){
password = new String(uptoken.getPassword());
}
int check = 0;
try {
check = loginService.check(username, password);
} catch (UserNotExitingException e) {
throw new UnknownAccountException(e.getMessage(),e);
}catch (PasswordErrorException e){
throw new IncorrectCredentialsException(e.getMessage(), e);
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(check,password,getName());
return info;
}
}
修改ShiroConfig
其他配置securityManager、ShiroFilterFactoryBean ,realm的配置这里省略,重点要加上以下代码——由于shiro使用的是AOP注解方式完成对上述两个注解的使用,这里要配置一个注解通知器才能正常使用
/**
* Shiro的配置类
* @author dhcurry
*
*/
@Configuration
public class ShiroConfig {
.......
// 上述内容省略
/**
* 开启Shiro注解通知器
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
@Qualifier("securityManager") SecurityManager securityManager)
{
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
创建一个controller资源限制访问
@Controller
@RequestMapping("/back")
public class SystemController extends BaseController {
@Autowired
private MenuService menuService;
@RequiresPermissions("system:view")
@RequiresRoles("admin")
@GetMapping("/index")
public String getSystem(ModelMap modelMap){
// 获取用户信息
UserInfoBean user = getCurrentUser();
// 获取菜单信息
List<MenuVO> menus = menuService.getAllMenuForShow();
modelMap.put("user",user);
modelMap.put("menus", menus);
return "back/index";
}
}