SpringBoot集成Shiro实现认证和授权

shiro介绍请移步shiro官网

1.创建springboot项目

使用spring初始化器创建即可

2.引入相关依赖

<!-- shiro依赖 -->
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-spring-boot-starter</artifactId>
  <version>1.5.3</version>
</dependency>
复制代码

3.配置shiro

写在前面,三个重要概念

Subject:代表当前用户,可以是一个人,也可以是第三方服务。在单应用中,可将其视为User的同义词。
SecurityManager:管理所有Subject,对于 Web 应用一般使用DefaultWebSecurityManager。
Realms:用于进行权限信息的验证,我们自己实现。是一个执行者,负责真正的认证和鉴权。

  1. 创建配置类

package com.almond.springbootshiro.common.config;

import com.almond.springbootshiro.common.realms.MyRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

/**
 * shiro配置类
 */
@Configuration
public class ShiroConfig {
    // 1.创建shiroFilter 拦截所有请求
    @Bean
    public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager defaultWebSecurityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        Map<String, String> map = new LinkedHashMap<>();
        //设置系统公共资源 不需要认证和授权的资源
        map.put("/user/login", "anon");
        map.put("/user/register", "anon");

        map.put("/register.jsp", "anon");
        //设置系统受限资源 需要认证和授权的资源
        map.put("/**", "authc"); //authc 请求需要授权和认证

        //设置默认的资源路径
        shiroFilterFactoryBean.setLoginUrl("/login.jsp");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }

    // 2.创建安全管理器
    @Bean
    public DefaultWebSecurityManager securityManager(Realm realm) {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        //安全管理器设置realm
        defaultWebSecurityManager.setRealm(realm);
        return defaultWebSecurityManager;
    }

    // 3.创建自定义的realm
    @Bean
    public Realm realm(HashedCredentialsMatcher hashedCredentialsMatcher) {
        MyRealm myRealm = new MyRealm();
        // 修改凭证校验匹配器
        myRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        return myRealm;
    }
}
复制代码
  1. 创建自定义realm

package com.almond.springbootshiro.common.realms;

import com.almond.springbootshiro.mapper.TUserMapper;
import com.almond.springbootshiro.po.TUsers;
import lombok.extern.slf4j.Slf4j;
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.apache.shiro.util.ByteSource;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;

@Slf4j
public class MyRealm extends AuthorizingRealm {

    @Resource
    private TUserMapper tUserMapper;

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        log.info("===============================");
        //获取主身份信息
        String principal = (String) principalCollection.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        //添加身份权限
        authorizationInfo.addRole("user");
        //添加资源权限
        List<String> strings = Arrays.asList("sys:user:add", "sys:user:update");
        authorizationInfo.addStringPermissions(strings);
        return authorizationInfo;
    }

    //验证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String principal = (String) authenticationToken.getPrincipal();
        TUsers users = tUserMapper.getOne(principal);
        if (null == principal || "".equals(principal)) {
            // 推荐抛出自定义异常
            throw new RuntimeException("token信息缺失");
        }
        if (principal.equals(users.getUsername())) {
            if (null == users.getPassword() || "".equals(users.getPassword())) {
                // 推荐抛出自定义异常
                throw new RuntimeException("用户信息缺失");
            }
            return new SimpleAuthenticationInfo(principal, users.getPassword(), ByteSource.Util.bytes(users.getSlat()), getName());
        }
        return null;
    }
}
复制代码
  1. 盐工具类

package com.almond.springbootshiro.common.utils;

import java.util.Random;

public class SaltUtil {
    public static String getSalt(Integer n) {
        StringBuffer salt = new StringBuffer();
        char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789{}@!#$%^&*()<>?|".toCharArray();
        for (int i = 0; i < n; i++) {
            char aChar = chars[new Random().nextInt(chars.length)];
            salt.append(aChar);
        }
        return salt.toString();
    }

    public static void main(String[] args) {
        String salt = getSalt(5);
        System.out.println(salt);
    }
}
复制代码

4.前端页面,使用jsp

  1. 主页

<%@page contentType="text/html; UTF-8" pageEncoding="UTF-8" isELIgnored="false" %>
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<h1>主页</h1>
<a href="${pageContext.request.contextPath}/user/logout">退出登录</a>
<ul>
    <shiro:hasAnyRoles name="user,admin">
        <li><a href="${pageContext.request.contextPath}/resource/one">资源一(user&&admin可见)</a></li>
        <ul>
            <shiro:hasPermission name="sys:user:add">
                <li>添加</li>
            </shiro:hasPermission>
            <shiro:hasPermission name="sys:user:update">
                <li>修改</li>
            </shiro:hasPermission>
            <shiro:hasPermission name="sys:user:*">
                <li>查询</li>
            </shiro:hasPermission>
        </ul>
    </shiro:hasAnyRoles>
    <shiro:hasRole name="admin">
        <li><a href="${pageContext.request.contextPath}/resource/two">资源二(admin可见)</a></li>
        <li><a href="${pageContext.request.contextPath}/resource/three">资源二(admin可见)</a></li>
    </shiro:hasRole>
</ul>
</body>
</html>
复制代码
  1. 登录表单

<%@page contentType="text/html; UTF-8" pageEncoding="UTF-8" isELIgnored="false" %>
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<h1>登录</h1>
<form action="${pageContext.request.contextPath}/user/login" method="post">
    用户名: <input name="username" type="text"/><br/>
    密码: <input name="password" type="password"/><br/>
    <input value="登录" type="submit"/>
</form>
</body>
</html>
复制代码
  1. 注册表单

<%@page contentType="text/html; UTF-8" pageEncoding="UTF-8" isELIgnored="false" %>
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<h1>注册</h1>
<form action="${pageContext.request.contextPath}/user/register" method="post">
    用户名: <input type="text" name="username" /><br />
    密码: <input type="password" name="password" /><br />
    <input type="submit" value="注册">
</form>
</body>
</html>