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标签控制显示

原文地址:https://www.cnblogs.com/alanchenjh/p/12341352.html