栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Shiro 入门篇01

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Shiro 入门篇01

1.github:https://github.com/yezhimincxvxb/shiro/tree/master/shiro01 2.SpringBoot + Mybatis-Plus + Shiro + Thymeleaf 版本依赖(父工程)


    4.0.0

    
        org.springframework.boot
        spring-boot-starter-parent
        2.5.0
         
    

    com.yzm
    shiro
    0.0.1-SNAPSHOT
    pom
    shiro
    Demo project for Spring Boot

    
    	
        common
        
        shiro01
        shiro02
        shiro03
        shiro04
        shiro05
        shiro06
    

    
        UTF-8
        UTF-8
        1.8
    

    
        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        

        
        
            mysql
            mysql-connector-java
        

        
        
            com.baomidou
            mybatis-plus-boot-starter
            3.3.2
        
        
        
            com.baomidou
            mybatis-plus-generator
            3.3.2
        
        
        
            org.freemarker
            freemarker
            2.3.30
        

        
        
            org.projectlombok
            lombok
        

        
        
            org.apache.commons
            commons-lang3
        

        
        
            com.alibaba
            fastjson
            1.2.62
        

        
        
            org.apache.shiro
            shiro-spring
            1.6.0
        

        
        
            org.springframework.boot
            spring-boot-starter-thymeleaf
        
    

    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                
                    ${java.version}
                    ${java.version}
                    ${project.build.sourceEncoding}
                
            
        
    


shiro01 入门篇子工程



    4.0.0

    
        com.yzm
        shiro
        0.0.1-SNAPSHOT
        ../pom.xml 
    

    shiro01
    0.0.1-SNAPSHOT
    jar
    shiro01
    Demo project for Spring Boot

    
        
            com.yzm
            common
            0.0.1-SNAPSHOT
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    



3 数据库表
CREATE TABLE `user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '用户名称',
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '用户密码',
   `salt` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '',
   `r_ids` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户表';

CREATE TABLE `role` (
  `r_id` int NOT NULL AUTO_INCREMENT,
  `r_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '角色名称',
  `r_desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '角色描述',
  `p_ids` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '',
  PRIMARY KEY (`r_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='角色表';

CREATE TABLE `permissions` (
  `p_id` int NOT NULL AUTO_INCREMENT,
  `p_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '权限名称',
  `p_desc` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '权限描述',
  PRIMARY KEY (`p_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='权限表';

用户表

角色表

权限表

4 目录结构

entity、mapper、service等都是mybatis代码自动生成器生成

application.yml
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/test3?useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
    username: root
    password: root

mybatis-plus:
  mapper-locations: classpath:/mapper
public class MyShiroRealm extends AuthorizingRealm {

    private final UserService userService;
    private final RoleService roleService;
    private final PermissionsService permissionsService;

    public MyShiroRealm(UserService userService, RoleService roleService, PermissionsService permissionsService) {
        this.userService = userService;
        this.roleService = roleService;
        this.permissionsService = permissionsService;
    }

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof UsernamePasswordToken;
    }

    
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String username = (String) principalCollection.getPrimaryPrincipal();
        // 查询用户,获取角色ids
        User user = userService.lambdaQuery().eq(User::getUsername, username).one();
        List roleIds = Arrays.stream(user.getRIds().split(","))
                .map(Integer::parseInt)
                .collect(Collectors.toList());

        // 查询角色,获取角色名、权限ids
        List roles = roleService.listByIds(roleIds);
        Set roleNames = new HashSet<>(roles.size());
        Set permIds = new HashSet<>();
        roles.forEach(role -> {
            roleNames.add(role.getRName());
            Set collect = Arrays.stream(
                    role.getPIds().split(",")).map(Integer::parseInt).collect(Collectors.toSet());
            permIds.addAll(collect);
        });

        // 获取权限名称
        List permissions = permissionsService.listByIds(permIds);
        List permNames = permissions.stream().map(Permissions::getPName).collect(Collectors.toList());

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.addRoles(roleNames);
        authorizationInfo.addStringPermissions(permNames);
        return authorizationInfo;
    }

    
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 获取用户名跟密码
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
        String username = usernamePasswordToken.getUsername();
        // 也可以这样获取
        //String username = (String) authenticationToken.getPrincipal();
        //String password = new String((char[]) authenticationToken.getCredentials());

        // 查询用户是否存在
        User user = userService.lambdaQuery().eq(User::getUsername, username).one();
        if (user == null) {
            throw new UnknownAccountException();
        }

        return new SimpleAuthenticationInfo(
                user.getUsername(),
                user.getPassword(),
                // 用户名 + 盐
                // 这里要跟EncryptUtils的一样
                ByteSource.Util.bytes(user.getUsername() + user.getSalt()),
                getName()
        );
    }
}
用户密码使用加密的

EncryptUtils.java
注册用户的时候加密,配置自定义的realm需要

package com.yzm.shiro01.utils;

import com.yzm.shiro01.entity.User;
import org.apache.shiro.crypto.RandomNumberGenerator;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;


public class EncryptUtils {

    private static final RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();
    public static final String ALGORITHM_NAME = "MD5"; // 散列算法
    public static final int HASH_ITERATIONS = 2; // 散列次数

    public static void encryptPassword(User user) {
        // 随机字符串作为salt因子,实际参与运算的salt我们还引入其它干扰因子
        user.setSalt(randomNumberGenerator.nextBytes().toHex());
        user.setPassword(new SimpleHash(
                ALGORITHM_NAME,
                user.getPassword(),
                // 用户名 + 盐
                ByteSource.Util.bytes(user.getUsername() + user.getSalt()),
                HASH_ITERATIONS
        ).toHex());
    }
}
6 ShiroConfig 配置类
package com.yzm.shiro01.config;

import com.yzm.shiro01.service.PermissionsService;
import com.yzm.shiro01.service.RoleService;
import com.yzm.shiro01.service.UserService;
import com.yzm.shiro01.utils.EncryptUtils;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
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;

@Configuration
public class ShiroConfig {

    private final UserService userService;
    private final RoleService roleService;
    private final PermissionsService permissionsService;

    public ShiroConfig(UserService userService, RoleService roleService, PermissionsService permissionsService) {
        this.userService = userService;
        this.roleService = roleService;
        this.permissionsService = permissionsService;
    }

    
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName(EncryptUtils.ALGORITHM_NAME);
        hashedCredentialsMatcher.setHashIterations(EncryptUtils.HASH_ITERATIONS);
        //true加密用的hex编码,false用的base64编码;默认true,本实例是toHex,可以查看EncryptUtils
        //hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
        return hashedCredentialsMatcher;
    }
    
    
    @Bean(name = "simpleRealm")
    public MyShiroRealm simpleShiroRealm() {
        MyShiroRealm myShiroRealm = new MyShiroRealm(userService, roleService, permissionsService);
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myShiroRealm;
    }

    
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 配置单个realm
        securityManager.setRealm(simpleShiroRealm());
        return securityManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilter() {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager());
        // setLoginUrl 如果不设置值,默认会自动寻找Web工程根目录下的"/login.jsp"页面 或 "/login" 映射
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 设置无权限时跳转的 url
        shiroFilterFactoryBean.setUnauthorizedUrl("/401");

        Map definitionMap = new linkedHashMap<>();
        definitionMap.put("/home", "anon");
        definitionMap.put("/login", "anon");
        definitionMap.put("/401", "anon");
        definitionMap.put("/register", "anon");
        definitionMap.put("/doLogin", "anon");
        //使用shiro默认的退出
        definitionMap.put("/logout", "logout");

        //拦截器perms表示需要拥有对应的权限才可以访问
        definitionMap.put("/user/select", "perms[user:select]");
        definitionMap.put("/user/create", "perms[user:create]");
        definitionMap.put("/user/update", "perms[user:update]");
        definitionMap.put("/user/delete", "perms[user:delete]");
        //拦截器perms[perms1,perms2]可以有多个参数,用逗号隔开,表示需要同时拥有多个权限,缺少其中一个都会被拒绝访问
        definitionMap.put("/user/createAndUpdate", "perms[user:create,user:update]");
        //拦截器roles表示需要拥有对应的角色才可以访问,跟perms一样可以拥有多个参数
        definitionMap.put("/user/**", "roles[USER]");

        //同一url可以有多个拦截器
        definitionMap.put("/admin/select", "roles[ADMIN],perms[admin:select]");
        definitionMap.put("/admin/create", "perms[admin:create]");
        definitionMap.put("/admin/update", "perms[admin:update]");
        definitionMap.put("/admin/delete", "perms[admin:delete]");
        definitionMap.put("/admin/**", "roles[ADMIN]");
        definitionMap.put("/**", "authc");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(definitionMap);
        return shiroFilterFactoryBean;
    }
}

7 控制器

AdminController.java

package com.yzm.shiro01.controller;

import org.apache.shiro.SecurityUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/admin")
public class AdminController {

    @GetMapping
    public Object admin() {
        return SecurityUtils.getSubject().getPrincipal();
    }

    @GetMapping("select")
    public Object select() {
        return "Select";
    }

    @GetMapping("create")
    public Object create() {
        return "Create";
    }

    @GetMapping("update")
    public Object update() {
        return "Update";
    }

    @GetMapping("delete")
    public Object delete() {
        return "Delete";
    }
}

HomeController.java

package com.yzm.shiro01.controller;

import com.yzm.common.entity.HttpResult;
import com.yzm.shiro01.entity.User;
import com.yzm.shiro01.service.UserService;
import com.yzm.shiro01.utils.EncryptUtils;
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.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class HomeController {

    private final UserService userService;

    public HomeController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("login")
    public String login() {
        return "login";
    }

    @GetMapping("home")
    public Object home() {
        return "home";
    }

    @GetMapping("401")
    public Object notRole() {
        return "401";
    }

    @PostMapping("register")
    @ResponseBody
    public Object register(@RequestParam String username, @RequestParam String password) {
        User user = new User();
        user.setUsername(username);
        user.setPassword(password);
        // 密码加密
        EncryptUtils.encryptPassword(user);
        userService.save(user);
        return HttpResult.ok();
    }

    @PostMapping("doLogin")
    @ResponseBody
    public Object doLogin(@RequestParam String username, @RequestParam String password, boolean rememberMe) {
        try {
            UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
            // 记住我在下一篇介绍,先无视
            if (rememberMe) usernamePasswordToken.setRememberMe(true);
            Subject subject = SecurityUtils.getSubject();
            subject.login(usernamePasswordToken);
        } catch (IncorrectCredentialsException ice) {
            return HttpResult.error("password error!");
        } catch (UnknownAccountException uae) {
            return HttpResult.error("username error!");
        }
        return HttpResult.ok();
    }
}

UserController .java

package com.yzm.shiro01.controller;

import org.apache.shiro.SecurityUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("//user")
public class UserController {

    @GetMapping
    public Object user() {
        return SecurityUtils.getSubject().getPrincipal();
    }

    @GetMapping("select")
    public Object select() {
        return "Select";
    }

    @GetMapping("create")
    public Object create() {
        return "Create";
    }

    @GetMapping("update")
    public Object update() {
        return "Update";
    }

    @GetMapping("delete")
    public Object delete() {
        return "Delete";
    }

    @GetMapping("createAndUpdate")
    public Object createAndUpdate() {
        return "Create And Update";
    }
}
8 xxx.html页面(thymeleaf)

home.html
把请求接口写在home里面。方便测试




    
    首页


注册

admin

Admin角色

Admin角色,拥有 select 权限

Admin角色,拥有 create 权限

Admin角色,拥有 update 权限

Admin角色,拥有 delete 权限

user

User角色

User角色,拥有 select 权限

User角色,拥有 create 权限

User角色,拥有 update 权限

User角色,拥有 delete 权限

User角色,拥有 create and update 权限

login.html




    
    登录页


用户名密码登录

Remember me on this computer.

401.html




    
    401


抱歉,你无权访问

9 注册访问

访问localhost:8080/home
注册管理员用户admin、普通用户yzm

角色信息跟权限信息都是直接修改数据库的,最终数据如上面的3数据库表。admin是有双重身份的

此时还在/home页面点击admin自动跳转到/login登录页面
这是 shiroFilterFactoryBean.setLoginUrl("/login"); 设置的,未登录跳转到登录页面

用yzm普通用户登录,再打开窗口访问首页/home,点击admin


由于shiroFilterFactoryBean.setUnauthorizedUrl("/401"); 访问没有权限的接口自动跳转到401

10 默认拦截器

org.apache.shiro.web.filter.mgt.DefaultFilter

    anon(AnonymousFilter.class), // 匿名访问
    authc(FormAuthenticationFilter.class), // 需要认证才访问
    authcBasic(BasicHttpAuthenticationFilter.class),
    authcBearer(BearerHttpAuthenticationFilter.class),
    logout(LogoutFilter.class), 
    noSessionCreation(NoSessionCreationFilter.class),
    perms(PermissionsAuthorizationFilter.class), // 具有权限才能访问
    port(PortFilter.class),
    rest(HttpMethodPermissionFilter.class),
    roles(RolesAuthorizationFilter.class), // 拥有角色才能访问
    ssl(SslFilter.class),
    user(UserFilter.class), // 认证过或使用记住我功能都可以访问
    invalidRequest(InvalidRequestFilter.class);

拦截器由上到下的执行顺序(执行一次)
例如,访问/user/select 只会执行perms,不会再去执行roles

definitionMap.put("/user/select", "perms[user:select]");
definitionMap.put("/user/**", "roles[USER]");

debug perms、roles拦截器,请求/user/select接口,只执行了perms


再例如,

// 访问/user/delete,先执行的roles通过,不会去执行perms,yzm没有delete权限也照样可以访问,这种是不对
definitionMap.put("/user/**", "roles[USER]");
definitionMap.put("/user/delete", "perms[user:delete]");

拦截器从左到右的执行顺序,依次执行,全部通过才可以访问
例如,

// 先执行roles,再执行perms,两个都成功,可以访问
definitionMap.put("/user/select", "roles[USER],perms[user:select]");
// 先执行roles,发现角色不对,不再去执行perms,直接跳转到401
definitionMap.put("/user/create", "roles[ADMIN],perms[user:create]");
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/325421.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号