一、概述
这篇文章主要总结自己对shiro框架实现登录认证和shiro授权控制过程的学习、实践经验。shiro整合springboot框架实现的过程。现在的工作不会涉及到这些开源框架,有空还是学习、总结一下,提升自己的记忆。
二、shiro过程概述
“Shiro不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过相应的接口注入Shiro即可”
从外部应用程序的角度来看,shiro完成工作的流程如下图
Subject:主体,代表当前用户,所有的Subject都会绑定到SecurityManager,与Subject所有交互都会委托给SecurityManager。
SecurityManager:安全管理器,即所有与安全相关的操作都会与SecurityManager交互;管理着所有的Subject,可以看出它是shiro的核心。
Realm:域,Shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较确定用户身份。也需要从Realm得到用户相应的角色/权限验证用户是否能进行操作。可以把Realm看成DataSources。
三、登录认证/授权
1、收集用户身份/凭证,即用户名和密码。
2、调用Subject.login进行登录,如果失败,可以根据相应的异常,提示用户错误信息。
3、自定义用户Realm类,进行用户认证、授权管理。
Controller层
@RequestMapping(value = "/login.do")
public String login(String name, String password, Model model){
//1.获取Subject
Subject subject = SecurityUtils.getSubject();
String password_secrety = EncryptUtil.encryptMD5(password);
//2.封装用户数据
UsernamePasswordToken token = new UsernamePasswordToken(name,password_secrety);
//3.执行登录方法
try {
subject.login(token);
//登录成功
return "index";
} catch (UnknownAccountException e) {
//登录失败:用户名不存在
model.addAttribute("msg", "用户名不存在");
return "login";
}catch (IncorrectCredentialsException e) {
//登录失败:密码错误
model.addAttribute("msg", "密码错误");
return "login";
}
}
自定义Realm实现(public class UserRealm extends AuthorizingRealm)
public class UserRealm extends AuthorizingRealm {
@Autowired
private IUserService userSerivce;
/**
* 执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
//给资源进行授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//添加资源的授权字符串
// info.addStringPermission("user:add");
//获取当前登录用户
Subject subject = SecurityUtils.getSubject();
User user = (User)subject.getPrincipal();
// 从数据库获取到权限编码,如user:add,这里只实践权限编码的方式
List permissionList=null;
//到数据库查询当前登录用户的授权字符串
permissionList=userSerivce.findAllPermByUserId(user.getUser_id());
info.addStringPermissions(permissionList);
return info;
}
/**
* 执行认证逻辑
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
//编写shiro判断逻辑,判断用户名和密码
//1.判断用户名
UsernamePasswordToken token = (UsernamePasswordToken)arg0;
User user = userSerivce.findUserByName(token.getUsername());
if(user==null){
//用户名不存在
return null;//shiro底层会抛出UnKnowAccountException
}
//2.判断密码
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
}
编写Shiro配置类(public class ShiroConfig),主要将项目资源初始化、资源过滤等进行配置
@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 filterMap = new LinkedHashMap();
/*filterMap.put("/add", "authc");
filterMap.put("/update", "authc");*/
// filterMap.put("/testThymeleaf", "anon");
//放行login.html页面,不然不能登录
filterMap.put("/login.do", "anon");
//授权过滤器,把需要限制权限的请求按钮都在这边过滤
//注意:当前授权拦截后,shiro会自动跳转到未授权页面
filterMap.put("/addUser.do", "perms[user:add]"); //通过该方法,对每一个方法进行权限控制
// filterMap.put("/update", "perms[user:update]");
filterMap.put("/*", "authc");
//修改调整的登录页面
shiroFilterFactoryBean.setLoginUrl("/logout.do");
//设置未授权提示页面
shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth.do");
/**
* 配置信息加入shiro的过滤器中
*/
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();
}
/**
* 配置ShiroDialect,用于thymeleaf和shiro标签配合使用
*/
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
}