Young87

SmartCat's Blog

So happy to code my life!

游戏开发交流QQ群号60398951

当前位置:首页 >跨站数据测试

Spring Boot学习笔记(二十一)Spring Boot Shiro 实例

Spring Boot学习笔记(二十一)Spring Boot Shiro 实例

放代码ing~ 搬知识ing~

新建项目
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
项目结构
在这里插入图片描述
pom.xml 需要的依赖:

   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
   </dependency>

   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-test</artifactId>
   </dependency>

   <dependency>
       <groupId>org.mybatis.spring.boot</groupId>
       <artifactId>mybatis-spring-boot-starter</artifactId>
   </dependency>

   <dependency>
       <groupId>org.apache.shiro</groupId>
       <artifactId>shiro-web</artifactId>
       <version>1.4.0</version>
   </dependency>

   <dependency>
       <groupId>org.apache.shiro</groupId>
       <artifactId>shiro-spring</artifactId>
       <version>1.4.0</version>
   </dependency>

   <dependency>
       <groupId>org.projectlombok</groupId>
       <artifactId>lombok</artifactId>
       <optional>true</optional>
   </dependency>

pom.xml 文件:

<?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.2.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>wen</groupId>
    <artifactId>shiro</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>shiro</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.4.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

我们一般都会给用户 (User) 角色(Role: 管理者、使用者)设置用户权限(Permission: 读和写、只读),所以先建实体类 Permission.java

package wen.shiro.bean;

public class Permission {

    private Long id;
    private String permission;

    public Permission(Long id, String permission) {
        this.id = id;
        this.permission = permission;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getPermission() {
        return permission;
    }

    public void setPermission(String permission) {
        this.permission = permission;
    }
}

Role.java,其中有对于权限的集合Set,即只读权限的用户添加一个Permission,读和写的用户添加两个权限:

package wen.shiro.bean;

import java.util.Set;

public class Role {

    private Long id;
    private String role;
    private Set<Permission> permissions;

    public Role(Long id, String role, Set<Permission> permissions) {
        this.id = id;
        this.role = role;
        this.permissions = permissions;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    public Set<Permission> getPermissions() {
        return permissions;
    }

    public void setPermissions(Set<Permission> permissions) {
        this.permissions = permissions;
    }
}

User.java 用户类,同样地,一个用户类有Role的集合Set,一个用户可以有多个角色(管理者和访问者:添加两个Role,访问者:添加一个Role)

package wen.shiro.bean;

import java.util.Set;

public class User {

    private Long id;
    private String username;
    private String password;
    private Set<Role> roles;

    public User(Long id, String username, String password, Set<Role> roles) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.roles = roles;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Set<Role> getRoles() {
        return roles;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }
}

LoginService.java 和 对应实现类 LoginServiceImpl.java

package wen.shiro.service;

import wen.shiro.bean.User;

public interface LoginService {

    User getUserByUsername(String username);

}

package wen.shiro.service.impl;

import org.springframework.stereotype.Service;
import wen.shiro.bean.Permission;
import wen.shiro.bean.Role;
import wen.shiro.bean.User;
import wen.shiro.service.LoginService;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

@Service
public class LoginServiceImpl implements LoginService {

    Map<String, User> map = new HashMap<>();

    public LoginServiceImpl() {

        // 设置用户权限:query 和 add
        Permission queryPermission = new Permission(1L, "query");
        Permission addPermission = new Permission(2L, "add");

        // 用户一角色权限设置:读和写
        Set<Permission> permissionSetOne = new HashSet<>();
        permissionSetOne.add(queryPermission);
        permissionSetOne.add(addPermission);

        Role role = new Role(1L, "admin", permissionSetOne);
        Set<Role> roleSet = new HashSet<>();
        roleSet.add(role);

        User user = new User(1L, "admin_user", "123456", roleSet);
        map.put(user.getUsername(), user);

        // 用户二角色权限设置:只读权限
        Set<Permission> permissionSetTwo = new HashSet<>();
        permissionSetTwo.add(queryPermission);

        Role role1 = new Role(2L, "user", permissionSetTwo);
        Set<Role> roleSet1 = new HashSet<>();
        roleSet1.add(role1);

        User user1 = new User(2L, "visitor_user", "123456", roleSet1);
        map.put(user1.getUsername(), user1);

    }

    @Override
    public User getUserByUsername(String username) {
        return map.get(username);
    }

}

接下来需要定义一个Realm类用于查询用户的角色和权限信息并记录到权限管理器SercurityManager
Realm.java

package wen.shiro.realm;

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.springframework.beans.factory.annotation.Autowired;
import wen.shiro.bean.Permission;
import wen.shiro.bean.Role;
import wen.shiro.bean.User;
import wen.shiro.service.LoginService;

public class CustomRealm extends AuthorizingRealm {

    @Autowired
    private LoginService loginService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        // 获取登录用户名
        String username = (String) principalCollection.getPrimaryPrincipal();
        // 根据用户名查询信息
        User user = loginService.getUserByUsername(username);
        // 添加角色和权限
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

        for (Role role : user.getRoles()) {
            // 添加角色
            simpleAuthorizationInfo.addRole(role.getRole());
            // 添加权限
            for (Permission permissions : role.getPermissions()) {
                simpleAuthorizationInfo.addStringPermission(permissions.getPermission());
            }
        }

        return simpleAuthorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) {
        // 在post请求之前先进行认证
        if (authenticationToken.getPrincipal() == null) {
            return null;
        }
        // 获取用户信息
        String username = authenticationToken.getPrincipal().toString();
        User user = loginService.getUserByUsername(username);
        if (user == null) {
            // 返回后会报出异常
            return null;
        } else {
            //验证 authenticationToken和 simpleAuthenticationInfo的信息
            SimpleAuthenticationInfo simpleAuthenticationInfo
                    = new SimpleAuthenticationInfo(username, user.getPassword(), getName());
            return simpleAuthenticationInfo;
        }
    }
}

到这里已经实现了用户认证模块,那么我们接下来要做的就是把Realm和权限管理器SercurityManager放入Spring容器:
ShiroConfig.java

package wen.shiro.config;

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.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import wen.shiro.realm.CustomRealm;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }

    // 将自定义的验证方式加入容器
    @Bean
    public CustomRealm shiroRealm() {
        CustomRealm customRealm = new CustomRealm();
        return customRealm;
    }

    // 权限管理:Realm的管理认证
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(shiroRealm());
        return securityManager;
    }

    // Filter 工厂,设置对应的过滤条件和跳转条件
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String, String> map = new HashMap<>();
        // 登出
        map.put("/logout", "logout");
        // 对所有用户进行认证
        map.put("/**", "authc");
        // 登录
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 首页
        shiroFilterFactoryBean.setSuccessUrl("/index");
        // 认证失败不跳转
        shiroFilterFactoryBean.setUnauthorizedUrl("/error");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }

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

LoginController.java

package wen.shiro.controller;

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.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import wen.shiro.bean.User;

@RestController
public class LoginController {

    @RequestMapping("/login")
    public String login(User user) {
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(user.getUsername(), user.getPassword());
        try {
            subject.login(usernamePasswordToken);
        } catch (AuthenticationException e) {
            e.printStackTrace();
            return "账号或密码错误!";
        } catch (AuthorizationException e) {
            e.printStackTrace();
            return "没有权限";
        }
        return "login success";
    }

    @RequiresRoles("admin")
    @RequiresPermissions("add")
    @RequestMapping("/index")
    public String index() {
        return "This is the index after you've been authorized";
    }
}

注:如果现在直接开启项目访问浏览器而用户名或密码错误,也就是没有验证成功的话,控制台会报错,但是前端是看不到任何报错信息的,所以在这里定义一个用于拦截异常的类,用于返回给前端错误信息。
ExceptionCatcher.java

package wen.shiro.filter;

import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice
@Slf4j
public class ExceptionCatcher {

    @ExceptionHandler
    @ResponseBody
    public String ErrorHandler(AuthorizationException e) {
        return "没有通过权限验证";
    }
}

开启项目访问 http://localhost:8080/login?username=admin_user&password=123456
(在LoginServiceImpl.java 定义了用户名为admin_user, 密码为123456)
在这里插入图片描述
接着访问 http://localhost:8080/index
在这里插入图片描述
在之前的 LoginController.java 中:

    @RequiresRoles("admin")
    @RequiresPermissions("add")
    @RequestMapping("/index")
    public String index() {
        return "This is the index after you've been authorized";
    }

注解 @RequiresRoles("admin") 说明只有管理员才能得到返回 “This is the index after you’ve been authorized” 的结果,所以我们试一下 以visitor_user 的角色访问

http://localhost:8080/login?username=visitor_user&password=123456
在这里插入图片描述
接着再访问 http://localhost:8080/index
在这里插入图片描述
基本功能完成 😉 ~
源码:https://github.com/StephaineWYT/shiro

除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog

上一篇: 加速布局无服务器生态,腾讯云与Serverless.com达成全球战略合作!

下一篇: 从入门到资深

精华推荐