shiro入门(快速搭建、整合Springboot、登录拦截、用户认证、整合mybatis、用户授权、整合thymeleaf)
查看官方文档shiro的10分钟快速搭建
http://shiro.apache.org/10-minute-tutorial.html
github查看shiro源码
https://github.com/apache/shiro.git
导入依赖、新建shiro.ini和log4j.properties配置文件,log4j非必须,最后编写启动类
查看启动类QuickStart的源码,对比Security
shiro三大核心对象
Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。
Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
Realm:Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。
Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。
整合SpringBoot
编写Shiro配置类和Realm配置类
@Configuration public class ShiroConfig { //第三步 ShiroFilterFactoryBean @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultsecurityManager) { ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); //设置安全管理器 bean.setSecurityManager(defaultsecurityManager); return bean; } //第二步 DefaultWebSecurityManager @Bean(name="securityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //关联UserRealm securityManager.setRealm(userRealm); return securityManager; } //第一步 创建Realm对象 ,需要自定义类 @Bean public UserRealm userRealm(){ return new UserRealm(); } }
public class UserRealm extends AuthorizingRealm { //授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal){ return null; } //认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token){ return null; } }
登录拦截(授权实现)
编写登录页及controller
<h1>登录</h1>
<form th:action="@{/authentication}">
用户名:<input type="password" name="username">
密码:<input type="password" name="password">
<p><input type="submit"></p>
</form>
<p th:text="${msg}" style="color:red"></p>
@RequestMapping("/user/login") public String update(){ return "user/login"; }
shiro配置类中加入登录拦截器,没有权限则跳转到登录页
//第三步 ShiroFilterFactoryBean @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultsecurityManager) { ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); //设置安全管理器 bean.setSecurityManager(defaultsecurityManager); //添加Shiro的内置过滤器 /* anon:无需认证即可访问 authc:认证访问 user:使用remember me功能才能访问 perms:拥有对某个资源的权限才能访问 role:拥有某个角色权限才能访问 */ Map<String,String> filterMap = new LinkedHashMap<>(); //设置权限 filterMap.put("/user/add","authc"); filterMap.put("/user/update","authc"); bean.setFilterChainDefinitionMap(filterMap); //设置登录请求 bean.setLoginUrl("/user/login"); return bean; }
测试效果,点击主页的add或update会跳转至登录页
用户认证
配置登录提交用户名密码的认证,先配置controller,再配置Realm认证类
@RequestMapping("/authentication") public String authentication(String username,String password,Model model){ //获取当前的用户 Subject subject = SecurityUtils.getSubject(); //封装用户的登录数据 UsernamePasswordToken token = new UsernamePasswordToken(username,password); //执行登录方法,捕获异常 try { subject.login(token); return "index"; } catch (UnknownAccountException e) { model.addAttribute("msg","用户名错误"); //用户名不存在 e.printStackTrace(); return "/user/login"; }catch (IncorrectCredentialsException e) { model.addAttribute("msg","密码错误"); //密码不存在 e.printStackTrace(); return "/user/login"; } }
//认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token){ System.out.println("====执行了认证方法doGetAuthenticationInfo===="); //用户名密码 String username = "root"; String password = "123"; //此处AuthenticationToken为全局对象 subject.login()时传入 UsernamePasswordToken userToken = (UsernamePasswordToken) token; if(!userToken.getUsername().equals(username)){ return null;//抛出异常 UnknownAccountException } return new SimpleAuthenticationInfo("",password,""); }
整合Mybatis
mybatis与springboot整合见:SpringBoot中的数据访问(JDBC、Druid、Mybatis整合)
整合后shiro从DB获取用户的数据,修改对应的认证过程
//注入UserDao
@Autowired
private UserDao userDao;
//认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token){ System.out.println("====执行了认证方法doGetAuthenticationInfo===="); //此处AuthenticationToken为全局对象 subject.login()时传入 UsernamePasswordToken userToken = (UsernamePasswordToken) token; List<User> userList = userDao.selectUser(); for (User user:userList) { if(!userToken.getUsername().equals(user.getName())){ continue; }else{ return new SimpleAuthenticationInfo("",user.getPwd(),""); } } return null;//抛出异常 UnknownAccountException }
用户授权
User类新增perm字段,增加相应的测试数据
重新设置权限,同时设置未经授权的处理
//设置权限 filterMap.put("/user/add","perms[user:add]"); filterMap.put("/user/update","perms[user:update]");
//设置未经授权请求
bean.setUnauthorizedUrl("/noauth");
controller编写未经授权提示
@ResponseBody @RequestMapping("/noauth") public String unauthorized(){ return "未经授权"; }
在认证信息类返回对象中加入参数user,使授权类通过Subject获取user对象中的perm属性,使用perm属性进行用户权限设置
//授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal){ System.out.println("====执行了授权方法doGetAuthorizationInfo===="); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //拿到当前登录的对象 Subject subject = SecurityUtils.getSubject(); User user = (User)subject.getPrincipal(); //设置当前用户权限 info.addStringPermission(user.getPerm()); return info; } //认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token){ System.out.println("====执行了认证方法doGetAuthenticationInfo===="); //此处AuthenticationToken为全局对象 subject.login()时传入 UsernamePasswordToken userToken = (UsernamePasswordToken) token; List<User> userList = userDao.selectUser(); for (User user:userList) { if(!userToken.getUsername().equals(user.getName())){ continue; }else{ return new SimpleAuthenticationInfo(user,user.getPwd(),""); } } return null;//抛出异常 UnknownAccountException }
整合thymeleaf
加入依赖
<dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency>
shiro配置类中加入整合方法
//整合thymeleaf @Bean public ShiroDialect getShiroDialect(){ return new ShiroDialect(); }
认证通过后向shiro的session中加入属性,提供给前端显示按钮时做判断
//认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token){ System.out.println("====执行了认证方法doGetAuthenticationInfo===="); //此处AuthenticationToken为全局对象 subject.login()时传入 UsernamePasswordToken userToken = (UsernamePasswordToken) token; List<User> userList = userDao.selectUser(); for (User user:userList) { if(!userToken.getUsername().equals(user.getName())){ continue; }else{ //认证通过后向shiro的session中加入属性,提供给前端显示按钮时做判断 Subject currentsub = SecurityUtils.getSubject(); Session session = currentsub.getSession(); session.setAttribute("loginUser",user); return new SimpleAuthenticationInfo(user,user.getPwd(),""); } } return null;//抛出异常 UnknownAccountException }
页面加入shiro申明,使用shiro标签控制显示