定义: Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。

核心组件: Subject, SecurityManager 和 Realms.

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实现。

  

Realm 认证授权:

login 认证授权:

springboot 不需要鉴权注解 springboot权限检验_自定义


前端代码

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>登陆</title>
    <link href="js/bootstrap-3.3.7-dist/css/bootstrap.min.css" rel="stylesheet" />
    <link href="css/login.css" rel="stylesheet" />
    <script src="js/login.js" type="text/javascript"></script>
</head>
<body>
<div class="loginLayout">
    <div class="loginHeader">
        <!-- logo -->
        <div class="login_logo"></div>

        <!-- 竖线 -->
        <img src="img/login_line.png"/>

        <!-- 标题 -->
        <div class="login_title">
            <div class="title"></div>
            <div class="title_2"></div>
        </div>
    </div>

    <!-- welcome字符 -->
    <div class="welcome_box">
        <div class="login_welcome"></div>
    </div>

    <!-- 登录框 -->
    <div class="loginBox">
        <form action="./login/auth" method="post" onsubmit="loginFun(this)">
            <div class="login_user">
                <input type="text" placeholder="请输入用户名" id="userName" name="userName" onkeyup="this.value=this.value.replace(/\s+/g,'')" />
            </div>
            <div class="login_pass">
                <input type="password" placeholder="请输入密码" id="password" name="password" onkeyup="this.value = this.value.replace(/[^\d.]/g,'')" />
            </div>
            <button type="submit">登录</button>
        </form>
</div>
</div>

<script src="js/jquery-3.4.1/jquery-3.4.1/jquery-3.4.1.min.js" type="text/javascript"></script>
<script src="js/bootstrap-3.3.7-dist/js/bootstrap.min.js" type="text/javascript"></script>
</body>
</html>

Login controller

/**
         * @Param: [request, response]
         * @Return: void
         * @Description: 登录
         */
        @RequestMapping(value = "/login/auth", method = RequestMethod.POST)
        public String loginPage(String userName, String password, HttpServletRequest request) throws IOException, ServletException {
            User user = new User();
            user.setFirmLoginName(userName);
            user.setFirmLoginPassword(password);
            // 根据用户名和密码创建token
            UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken//
                    (user.getFirmLoginName(), user.getFirmLoginPassword());
            // 获取 subject 认证主体
            Subject subject = SecurityUtils.getSubject();
            try {
                // 开始认证,这一步会跳到我们自定义的 Realm 中
                subject.login(usernamePasswordToken);
                User uses = userService.queryUser(userName);
                request.getSession().setAttribute("users", uses);
                if (uses.getFirmId().equals(HDAS_FIRM_USER_ID)) {
                    String adminPath = "/users/base.page";
                    return adminPath;
                } else {
                    String path = "/users/demo.page";
                    return path;
                }

            } catch (Exception e) {
                e.printStackTrace();
                request.getSession().setAttribute("users", user);
                request.setAttribute("error", "用户名或密码错误!");
                return "redirect:/firstLogin.page";
            }
        }

Realm

/**
 * @ClassName MyRealm
 * @Description 自定义realm
 * @Author Fetter
 * @Date 2020/6/30 10:35
 * @Version 1.0
 */
public class MyRealm extends AuthorizingRealm {
    @Resource
    private UserService userService;

    /**
     * @Description: 授权
     * @param:[principalCollection]
     * @return:org.apache.shiro.authz.AuthorizationInfo
     */

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        // 获取用户名
        String username = (String) principalCollection.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        // 给该用户设置角色,角色信息存在 firm_role 表中取
        authorizationInfo.setRoles(userService.getRoles(username));
        // 给该用户设置权限,权限信息存在 firm_permission 表中取
        authorizationInfo.setStringPermissions(userService.getPermissions(username));
        return authorizationInfo;
    }

    /**
     * @Description: 认证
     * @param:[authenticationToken]
     * @return:org.apache.shiro.authc.AuthenticationInfo
     */

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 根据 Token 获取用户名,如果您不知道该 Token 怎么来的,先可以不管,下文会解释
        String username = (String) authenticationToken.getPrincipal();
        // 根据用户名从数据库中查询该用户
        User user = userService.queryUser(username);
        if(user != null) {
            // 把当前用户存到 Session 中
            SecurityUtils.getSubject().getSession().setAttribute("user", user);
            // 传入用户名和密码进行身份认证,并返回认证信息
            AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user.getFirmLoginName(), user.getFirmLoginPassword(), "myRealm");
            return authcInfo;
        } else {
            return null;
        }
    }
    }

需要拦截认证授权的路径以及放行的资源

/**
 * @ClassName ShiroConfig
 * @Description 配置自定义realm
 * @Author Fetter
 * @Date 2020/6/30 10:45
 * @Version 1.0
 */
@Configuration
public class ShiroConfig {

    private static final Logger logger = LoggerFactory.getLogger(ShiroConfig.class);
    /**
     * 注入 Shiro 过滤器
     * @param securityManager 安全管理器
     * @return ShiroFilterFactoryBean
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        // 定义 shiroFactoryBean
        ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();

        // 设置自定义的 securityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 设置默认登录的 URL,身份认证失败会访问该 URL
        shiroFilterFactoryBean.setLoginUrl("/firstLogin.page");
        // 设置成功之后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/users/base.page");
        // 设置未授权界面,权限认证失败会访问该 URL
//          shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");

        // LinkedHashMap 是有序的,进行顺序拦截器配置
        Map<String,String> filterChainMap = new LinkedHashMap<>();

        // 配置可以匿名访问的地址,可以根据实际情况自己添加,放行一些静态资源等,anon 表示放行
        filterChainMap.put("/static/**", "anon");

        // 登录 URL 放行
        filterChainMap.put("/firstLogin.page", "anon");
        filterChainMap.put("/login/auth","anon");
        filterChainMap.put("**/dust/home*/**", "anon");
        filterChainMap.put("**/dust/parsingData*", "anon");
        filterChainMap.put("**/dust/homeMapList*.json", "anon");

        // “/user/admin” 开头的需要身份认证,authc表示要身份认证
        filterChainMap.put("/login/auth*", "authc");

        filterChainMap.put("/login/auth*/**", "authc");
        filterChainMap.put("/users/base.page*/**", "roles[admin]");
        filterChainMap.put("/users/index.page*/**", "authc");
        filterChainMap.put("/users/demo.page*/**", "authc");
        filterChainMap.put("/users/historyData*/**", "authc");
        filterChainMap.put("/users/monitoraData*/**", "authc");
        filterChainMap.put("/users/secretyKey*/**", "authc");
        filterChainMap.put("/users/records*/**", "authc");
        filterChainMap.put("/users/opsEntry*/**", "authc");

        // 配置 logout 过滤器
        filterChainMap.put("../logOut.page", "logout");

        // 设置 shiroFilterFactoryBean 的 FilterChainDefinitionMap
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
        return shiroFilterFactoryBean;
    }
    /**
     * 注入自定义的 Realm
     *
     * @return MyRealm
     */
    @Bean
    public MyRealm myAuthRealm() {
        MyRealm myRealm = new MyRealm();
        return myRealm;
    }

    /**
     * 注入安全管理器
     *
     * @return SecurityManager
     */
    @Bean
    public SecurityManager securityManager() {
        // 将自定义 Realm 加进来
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(myAuthRealm());
        return securityManager;
    }
}