Shiro的基本使用

Shiro

Apache Shiro是一个功能强大且易于使用的 Java 安全框架,可执行身份验证、授权、加密和会话管理。

Shiro的基本使用

  • Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
  • Authorization:授权,即权限验证,判断某个已经认证过的用户是否拥有某些权限访问某些资源,一般授权会有角色授权和权限授权;
  • SessionManager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的,web 环境中作用是和 HttpSession 是一样的;
  • Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

它有三个核心的组件:Subject, SecurityManagerRealms.

  • Subject:即当前的操作用户;

  • SecurityManager :它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。

  • Realms:Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。

官方的十分钟入门文档:https://shiro.apache.org/10-minute-tutorial.html

以下为入门文档中的例子文件:

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class Quickstart {

    private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);


    public static void main(String[] args) {

        DefaultSecurityManager securityManager = new DefaultSecurityManager();
        IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
        securityManager.setRealm(iniRealm);


        SecurityUtils.setSecurityManager(securityManager);

        //在几乎所有环境中,您都可以通过以下调用获取当前正在执行的用户:
        Subject currentUser = SecurityUtils.getSubject();

        //在用户与应用程序的当前会话期间向其提供内容,您可以获取他们的会话:(它不需要 HTTP 环境!)
        Session session = currentUser.getSession();
        session.setAttribute("someKey", "aValue");
        String value = (String) session.getAttribute("someKey");
        if (value.equals("aValue")) {
            log.info("Retrieved the correct value! [" + value + "]");
        }

        //对已知用户进行这些检查。我们Subject上面的实例代表当前用户,判断是否已经登录了
        if (!currentUser.isAuthenticated()) {
            UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
            token.setRememberMe(true);
            //如果登录尝试失败了怎么办,则会捕捉各种异常,并及时做出处理
            try {
                currentUser.login(token);
            } catch (UnknownAccountException uae) {
                log.info("There is no user with username of " + token.getPrincipal());
            } catch (IncorrectCredentialsException ice) {
                log.info("Password for account " + token.getPrincipal() + " was incorrect!");
            } catch (LockedAccountException lae) {
                log.info("The account for username " + token.getPrincipal() + " is locked.  " +
                        "Please contact your administrator to unlock it.");
            }

            catch (AuthenticationException ae) {

            }
        }

        //输出已登录的用户信息:(测试角色)
        log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");

        //测试其是否具有特定的作用:
        if (currentUser.hasRole("schwartz")) {
            log.info("May the Schwartz be with you!");
        } else {
            log.info("Hello, mere mortal.");
        }

        //查看他们是否有权对某种类型的实体采取行动:(测试输入权限)
        if (currentUser.isPermitted("lightsaber:wield")) {
            log.info("You may use a lightsaber ring.  Use it wisely.");
        } else {
            log.info("Sorry, lightsaber rings are for schwartz masters only.");
        }

        //查看用户是否有能力访问某个类型的特定实例:
        if (currentUser.isPermitted("winnebago:drive:eagle5")) {
            log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'.  " +
                    "Here are the keys - have fun!");
        } else {
            log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
        }

        //用户注销:
        currentUser.logout();

        System.exit(0);
    }
}

小总结:

---认证:

1、subject获取用户; SecurityUtils.getSubject();

2、UsernamePasswordToken 封装数据

3、登录; subject.login(token);

之后就进行一些登录中的某些异常判断。

----拦截:分为三步

1、创建realm对象;

2、efaultWebSecurityManager

3、ShiroFilterFactoryBean;所有授权拦截操作都这里配置

导入shiro依赖

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.7.1</version>
</dependency>

配置shiro

需要现有一个UserRealm:

package com.vxzx.config;

import com.vxzx.pojo.User;
import com.vxzx.server.UserServer;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;

public class UserRealm extends AuthorizingRealm {
    @Autowired
    UserServer userServer;

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了授权的方法:====》AuthorizationInfo");
        //将给用户进行授权,让用户具有一定的权限:   SimpleAuthorizationInfo
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //让所有用户都拥有user:add的权限:
//        info.addStringPermission("user:add");

        //拿到当前用户,让其自动识别数据库里面的拥有的权限:
        Subject subject = SecurityUtils.getSubject();
        //获取User:
        User principal = (User) subject.getPrincipal();
        //设置权限:
        info.addStringPermission(principal.getPerms());
        return info;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行了认证的方法:====》AuthenticationInfo");

        UsernamePasswordToken userToken = (UsernamePasswordToken)authenticationToken;
        //从数据库中获取用户信息:
        User user = userServer.query(userToken.getUsername());

        if (user==null){
            return null;
        }
        //密码认证:
        return new SimpleAuthenticationInfo(user,user.getPwd(),"");
    }
}

ShiroConfig:里面配置一些拦截的配置

package com.vxzx.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {
    //ShiroFilterFactoryBean:第三步(基于第二步)
    @Bean
    public ShiroFilterFactoryBean factoryBean(@Qualifier("securityManager")DefaultSecurityManager defaultSecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //关联第二步中的securityManager:
            //设置安全管理器:
        bean.setSecurityManager(defaultSecurityManager);

        //添加shiro的内置过滤器:
        /*
        *   anno : 无需认证就可以访问
        *   authc :必须认证才可以访问
        *   user : 必须拥有 记住我 才可以访问
        *   perms : 拥有对某个资源的权限
        *   roles : 拥有对某个角色的访问权
        * */
        Map<String, String> filters = new LinkedHashMap();
        filters.put("/add","authc");
        filters.put("/update","authc");
        //授权:未授权的情况下,或跳转到未授权的页面去;
        filters.put("/add","perms[user:add]");
        filters.put("/update","perms[user:update]");

        //过滤请求:
        bean.setFilterChainDefinitionMap(filters);

        //权限不够,跳转到专门页面去:
        bean.setUnauthorizedUrl("/unauthorized");

        //设置登录请求:
        bean.setLoginUrl("/tologin");

        //

        return bean;
    }

    //DefaultWebSecurityManager: 第二步(基于第一步)
    @Bean(name="securityManager")
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("user")UserRealm userRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //关联第一步中的userRealm:
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    //创建realm对象:第一步
    @Bean(name="user")
    public UserRealm userRealm(){
        return  new UserRealm();
    }

    //整合ShiroDialect, 可以整合Shiro Thymeleaf
    @Bean
    public ShiroDialect shiroThy(){
        ShiroDialect dialect = new ShiroDialect();
        return dialect;
    }
}

controller配置前端页面的跳转:

package com.vxzx.controller;

import com.vxzx.dao.UserMapper;
import com.vxzx.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class RouterC {
    @Autowired
    UserMapper userMapper;

    @RequestMapping({"/","index"})
    public String toIndex(Model model){
        model.addAttribute("msg","Hello,Shiro");
        return "index";
    }

    @RequestMapping("/add")
    public String add(){
        return "user/add";
    }

    @RequestMapping("/update")
    public String update(){
        return "user/update";
    }

    @RequestMapping("/tologin")
    public String tologin(){
        return "user/login";
    }

    @RequestMapping("/login")
    public String login(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","用户名不正确");
                return "/user/login";
            }catch (IncorrectCredentialsException e){
                model.addAttribute("msg","密码不正确");
                return "/user/login";
            }
    }

    @RequestMapping("/unauthorized")
    @ResponseBody
    public String unauthorized(){
        return "权限不够,无法访问!";
    }

    @RequestMapping("/logout")
    public String logout(){
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "user/login";
    }
}

其中还涉及到mybatis的配置及前端页面的配置,这些都自行配置就行,主要说明shiro的作用及配置。

上一篇:springboot整合shiro


下一篇:跨组件通信 (2)