Shiro

一、代码未动,依赖先行

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>

二、实现Realm类

package com.example.shiro1.shiro;

import com.example.shiro1.dto.PermissionDTO;
import com.example.shiro1.dto.RoleDTO;
import com.example.shiro1.dto.UserDTO;
import com.example.shiro1.entity.User;
import com.example.shiro1.service.RoleService;
import com.example.shiro1.service.UserService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * @author: kang
 * @Company: 康康小课堂
 * @DateTime: 2020-07-18-19:04
 * @Description: 描述
 */
public class MyShiroRealm extends AuthorizingRealm {

    private Logger logger = LoggerFactory.getLogger(MyShiroRealm.class);

    @Autowired
    private UserService userService;

    @Autowired
    private RoleService roleService;

    /**
     * 设置授权信息
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        logger.info("开始授权(doGetAuthorizationInfo)");
        //获取登录用户名
        String name = (String) principals.getPrimaryPrincipal();
        //根据用户名去数据库查询用户信息
        UserDTO user = userService.getUserDTOByName(name);
        //添加角色和权限
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        for(RoleDTO dto: user.getRoles()){
            //添加角色
            simpleAuthorizationInfo.addRole(dto.getRoleName());
            //添加权限
            for(PermissionDTO permission : dto.getPermissions()){
                simpleAuthorizationInfo.addStringPermission(permission.getName());
            }

        }
        return simpleAuthorizationInfo;
    }

    /**
     * 设置认证信息
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken authenticationToken) throws AuthenticationException {
        logger.info("开始认证(doGetAuthenticationInfo)");
        //加这一步的目的是在Post请求的时候会先进认证,然后再请求
        if (authenticationToken.getPrincipal() == null) {
            return null;
        }
        //获取用户信息
        String name = authenticationToken.getPrincipal().toString();
        UserDTO user = userService.getUserDTOByName(name);
        if (user == null) {
            //这里返回后会报出对应异常
            return null;
        }
        //这里验证authenticationToken和simpleAuthenticationInfo的信息
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, user.getPassword().toString(), getName());
        return simpleAuthenticationInfo;
    }
}

三、实现Shiro的配置类

package com.example.shiro1.shiro;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author: kang
 * @Company: 康康小课堂
 * @DateTime: 2020-07-18-19:16
 * @Description: 描述
 */
@Configuration
public class ShiroConfiguration {
    /**
     * anno:无需认证(登陆)可以访问
     * authc:必须认证才能访问
     * user:如果使用rememberMe的功能可以直接访问
     * perms:该资源必须得到资源权限可以访问
     * role:该资源必须得到角色权限才能访问
     */
    private Logger logger = LoggerFactory.getLogger(ShiroConfiguration.class);

    /**
     * Filter工厂,设置对应的过滤条件和跳转条件
     * @param securityManager
     * @return
     */
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
        logger.info("进入shiroFilter......");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //设置不需要拦截的路径
        Map<String,String> map = new LinkedHashMap<String, String>();
        //按顺序依次判断
        map.put("/static/**", "anon");

        /************************************初始化所有的权限信息开始******************************************/
        //这里可以直接配置访问所需权限
        //filterChainDefinitionMap.put("/user/list", "authc,perms[user:list]");
        //filterChainDefinitionMap.put("/user/add", "authc,perms[user:add]");
        map.put("/**", "authc");
        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
        map.put("/logout", "logout");

        /***************************************初始化所有的权限信息结束*********************************************/
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/main");
        //未授权界面
        shiroFilterFactoryBean.setUnauthorizedUrl("/error");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }

    /**
     * 将自己的验证方式加入容器
     * @return
     */
    @Bean
    public MyShiroRealm myShiroRealm(){
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        //这里可以设置缓存的机制
        return myShiroRealm;
    }

    /**
     * 权限管理,配置主要是Realm的管理认证
     * @return
     */
    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        return securityManager;
    }


    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}

注:配置ShiroConfiguration时可能会报错–
Incompatible types.
Required: java.lang.SecurityManager
Found: org.apache.shiro.web.mgt.DefaultWebSecurityManager

此时只需要手动导入如下包即可
import org.apache.shiro.mgt.SecurityManager;

四、配置LoginController

package com.example.shiro1.controller;

import com.example.shiro1.entity.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author: kang
 * @Company: 康康小课堂
 * @DateTime: 2020-07-18-19:32
 * @Description: 描述
 */
@Controller
public class LoginController {

    private Logger logger = LoggerFactory.getLogger(LoginController.class);

    /**
     * 进入登录界面
     * @return login
     */
    @GetMapping("/login")
    public String getLogin(){
        logger.info("进入login页面");
        return "login";
    }

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


    /***********************************************************************************************************/
    /****************************************          shiro功能练习        ************************************/
    /***********************************************************************************************************/
    /**
     * 点击登录后的操作
     * @param user 用户
     * @return
     */
    @PostMapping("/post/login")
    public String doLogin(@RequestBody User user) {
        logger.info("进入登录处理");
        //添加用户认证信息
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
                user.getUsername(),
                user.getPassword()
        );
        try {
              //进行验证,这里可以捕获异常,然后返回对应信息
              subject.login(usernamePasswordToken);
              //subject.checkRole("admin");
              //subject.checkPermissions("query", "add");
        } catch (AuthenticationException e) {
            e.printStackTrace();
            return "账号或密码错误!";
        } catch (AuthorizationException e) {
            e.printStackTrace();
            return "没有权限";
        }
        return "main";
    }

    /**
     * 注解验角色和权限
     */
    @RequiresRoles("超级管理")
    @RequiresPermissions("系统管理")
    @RequestMapping("/goMain")
    public String goMain() {
        return "main";
    }
}

注:常用注解

@RequiresAuthenthentication:表示当前Subject已经通过login进行身份验证;即 Subjec.isAuthenticated()返回 true
@RequiresUser:表示当前Subject已经身份验证或者通过记住我登录的,
@RequiresGuest:表示当前Subject没有身份验证或者通过记住我登录过,即是游客身份
@RequiresRoles(value = {“admin”,”user”},logical = Logical.AND):表示当前Subject需要角色admin和user
@RequiresPermissions(value = {“user:delete”,”user:b”},logical = Logical.OR):表示当前Subject需要权限user:delete或者user:b

五、整合Thymeleaf

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
                xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">

<head>
    <title>首页</title>
</head>
<body>
<h1>首页</h1>
<hr>
<ul>
    <li><a href="user/index">个人中心</a></li>
    <li><a href="vip/index">会员中心</a></li>
    <p shiro:hasPermission="svip"><li>这是svip能看到的p标签</li></p>
    <shiro:hasPermission name="svip"><li>这是svip能看到的</li></shiro:hasPermission>
    <li><a href="logout">退出登录</a></li>
</ul>
</body>
</html>

注:常用标签
shiro:guest
游客访问

user 标签:用户已经通过认证\记住我 登录后显示响应的内容
shiro:user
欢迎[shiro:principal/]登录 退出

authenticated标签:用户身份验证通过,即 Subjec.login 登录成功 不是记住我登录的
shiro:authenticted
用户[shiro:principal/] 已身份验证通过

notAuthenticated标签:用户未进行身份验证,即没有调用Subject.login进行登录,包括”记住我”也属于未进行身份验证
shiro:notAuthenticated
未身份验证(包括”记住我”)

principal 标签:显示用户身份信息,默认调用
Subjec.getPrincipal()获取,即Primary Principal
<shiro:principal property = “username”/>

hasRole标签:如果当前Subject有角色将显示body体内的内容
<shiro:hashRole name = “admin”>
用户[shiro:principal/]拥有角色admin

hasAnyRoles标签:如果Subject有任意一个角色(或的关系)将显示body体里的内容
<shiro:hasAnyRoles name = “admin,user”>
用户[shiro:pricipal/]拥有角色admin 或者 user

lacksRole:如果当前 Subjec没有角色将显示body体内的内容
<shiro:lacksRole name = “admin”>
用户[shiro:pricipal/]没有角色admin

hashPermission:如果当前Subject有权限将显示body体内容
<shiro:hashPermission name = “user:create”>
用户[shiro:pricipal/] 拥有权限user:create

lacksPermission:如果当前Subject没有权限将显示body体内容
<shiro:lacksPermission name = “org:create”>
用户[shiro:pricipal/] 没有权限org:create


   转载规则


《Shiro》 kang 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录