美文网首页
把shiro说清楚只需要很少的代码

把shiro说清楚只需要很少的代码

作者: 不知不怪 | 来源:发表于2023-01-21 13:44 被阅读0次

1.pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.7</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.gzz</groupId>
    <artifactId>20-spring-boot-shiro</artifactId>
    <version>0.1</version>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.11.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

2 配置类

package com.gzz.config;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
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 {
    public static final int HASH_TIME = 32;
    public static final String ALGORITHM_NAME = "MD5";

    @Autowired
    private UserRealm userRealm;

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean() {
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        
        matcher.setHashAlgorithmName(ALGORITHM_NAME);// 指定加密方式为MD5
        matcher.setHashIterations(HASH_TIME);// 加密次数
        matcher.setStoredCredentialsHexEncoded(true);
        userRealm.setCredentialsMatcher(matcher);
        
        securityManager.setRealm(userRealm);// 关联realm.
        bean.setSecurityManager(securityManager);// 设置 SecurityManager,安全管理器
        bean.setLoginUrl("/index");// 设置登录跳转页面
        bean.setUnauthorizedUrl("/unAuth");// 设置未授权提示页面
        /**
         * Shiro内置过滤器,可以实现拦截器相关的拦截器 常用的过滤器: 
         * anon:无需认证(登录)可以访问 
         * authc:必须认证才可以访问
         * user:如果使用rememberMe的功能可以直接访问 
         * perms:该资源必须得到资源权限才可以访问 
         * role:该资源必须得到角色权限才可以访问
         **/
        Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/login", "anon");
        filterMap.put("/user", "authc");
        filterMap.put("/system", "perms[system]");
//      filterMap.put("/static/**", "anon");
        filterMap.put("/**", "authc");
        filterMap.put("/logout", "logout");
        bean.setFilterChainDefinitionMap(filterMap);
        return bean;
    }

}

3.授权类

package com.gzz.config;

import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.stereotype.Component;

import com.gzz.sys.user.User;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class UserRealm extends AuthorizingRealm {

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        log.info("执行认证");
        // 获取当前登录对象
        Subject subject = SecurityUtils.getSubject();
        User user = (User) subject.getPrincipal();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 添加当前用户的角色权限,用于判断可以访问那些功能
        info.addStringPermissions(user.getPermissions());
        info.addRoles(user.getRoles());
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) {
        // 编写shiro判断逻辑,判断用户名和密码
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        log.info("按登录名从数据库读取授权信息:授权信息到少应包括用户及所关联的角色集和权限集,至于表如何设计完全取决于项的实际需求与Shiro关系很小");
        User user = DATABASE.get(token.getUsername());
        if (user == null) {
            throw new UnknownAccountException();
        }
        ByteSource credentialsSalt = ByteSource.Util.bytes(user.getName());// 加盐也不是必选项,可以无此参数以账号作为加密的盐值
        return new SimpleAuthenticationInfo(user, user.getPassword(), credentialsSalt, getName());
    }

    // 加密方法用于生成模拟数据:只有在保存用户密码到数据库时才会使用
    public static void main(String[] args) {
        System.out.println(new SimpleHash(ShiroConfig.ALGORITHM_NAME, "12345", ByteSource.Util.bytes("zhang"), ShiroConfig.HASH_TIME));
        System.out.println(new SimpleHash(ShiroConfig.ALGORITHM_NAME, "12345", ByteSource.Util.bytes("li"), ShiroConfig.HASH_TIME));
    }

    private final static Map<String, User> DATABASE = Map.of(//
            "zhang", new User("zhang", "b7d32295e4492a45d70bc34087890138", List.of("system", "test", "user"), Set.of("admin")), //
            "li", new User("li", "138232801d3b1f5587c88d897fdc15bb", List.of(), Set.of("user"))//
    );
}

4 测试案例

package com.gzz.sys.user;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Controller
public class UserController {

    @GetMapping({ "/index", "/" })
    public String index() {
        return "login.html";
    }

    @RequestMapping(value = "/login")
    public String login(String username, String password) {
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            subject.login(token);
            return "redirect:/main";
        } catch (UnknownAccountException e) {
            log.error("用户名不存在");
            return "redirect:login.html";
        } catch (IncorrectCredentialsException e) {
            log.error("密码输入有误");
            return "redirect:login.html";
        }
    }

    @RequestMapping(value = "/main")
    public String main() {
        User user = (User) SecurityUtils.getSubject().getPrincipal();
        log.info("用户:{}", user);
        return "main.html";
    }

    @RequestMapping("/logout")
    public String logout() {
        Subject subject = SecurityUtils.getSubject();
        if (subject != null) {
            subject.logout();
        }
        return "redirect:/main.html";
    }

    @RequestMapping("/unAuth")
    public String unAuth() {
        return "unAuth.html";
    }

    @RequiresRoles("admin")
    @RequestMapping("/system")
    public String system() {
        return "system.html";
    }

    @RequiresPermissions("user:save,update")
    @RequestMapping("/user")
    public String user() {
        return "user.html";
    }

}

5.User类

package com.gzz.sys.user;

import java.util.List;
import java.util.Set;
import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class User {
//  private Integer id;
    private String name;
    private String password;
    private List<String> permissions;// 权限集
    private Set<String> roles;// 角色集

}

6 演示

1674366018127.png
1674366018132.png
http://localhost:8080/
分别使用zhang li 密码12345登录 查看资源与日志信息使用理解
7 源码
[源码](https://gitee.com/gao_zhenzhong/spring-boots/tree/master/21-security/21-spring-boot-shiro

相关文章

网友评论

      本文标题:把shiro说清楚只需要很少的代码

      本文链接:https://www.haomeiwen.com/subject/lzehhdtx.html