SpringBoot integra el código de caso completo de Shiro

SpringBoot integra el código de caso completo de Shiro

1. API central de Shiro

Asunto Usuario principal
SecurityManager Administrador de seguridad
Puente de datos de conexión shiro del reino

2. Estructura del proyecto y estructura de la base de datos

Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí

3. Importación de dependencias

        <!--web启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--thymeleaf-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!--thymeleaf对shiro扩展,用于html中使用shiro标签-->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!--shiro-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.39</version>
        </dependency>
        <!--druid连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>
        <!--myBatis-pulus-generator-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.30</version>
        </dependency>

4. Utilice el generador de código del generador

Generador de códigos

package com.example.generator;

import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.util.Scanner;

public class CodeGenerator {
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("请输入" + tip + ":");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotBlank(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "!");
    }

    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        /**
         * 这里需要设定一下保存的地址是本项目下的/src/main/java
         */
        gc.setOutputDir(projectPath + "/shiro/src/main/java");
        gc.setAuthor("XYD");
        gc.setOpen(false);
         gc.setSwagger2(true); //实体属性 Swagger2 注解
        mpg.setGlobalConfig(gc);

        // 数据源配置
        /**
         * 设置数据库名称和数据库账户密码
         */
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/temporary?useUnicode=true&useSSL=false&characterEncoding=utf8& serverTimezone=UTC");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("12345");
        mpg.setDataSource(dsc);

        // 包配置
        /**
         * 设置生成文件保存地址,模块名为命令窗口输入的模块名
         */
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(scanner("模块名"));
        //自定义相对于java目录的地址
        pc.setParent("com.baomidou.ant");
        mpg.setPackageInfo(pc);

        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };

        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();

        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);

        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);

        strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix(pc.getModuleName() + "_");
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }
}

Modifique la configuración de conexión de la base de datos y la dirección de almacenamiento de generación de archivos dentro

5. Clase de entidad generada, objeto asignador, servicio

Usuario

@Data
@EqualsAndHashCode(callSuper = false)

public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    private String userName;

    private String password;

    private String perms;


}

UserMapper

public interface UserMapper extends BaseMapper<User> {

}

IUserService

public interface IUserService extends IService<User> {

}

UserServiceImpl

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

}

5. Configuración de la aplicación

spring.thymeleaf.cache=false

#数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/temporary?useUnicode=true&useSSL=false&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=12345
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

6.clase de configuración shiro

ShiroConfig

@Configuration
public class ShiroConfig {
/**
 * 创建三个Bean放入容器中
 * 1. ShiroFilterFactoryBean
 *
 * 2.DefaultWebSecurityManager
 *
 * 3.创建MyRealm
 */

    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){

        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //添加shiro内置过滤器
        /**
         * 常用过滤器
         *  anon 无需认证
         *  authc 必须认证后才能访问
         *  user 如果使用了rememberMe功能可以直接访问
         *  perms 该资源必须得到资源权限才能访问
         *  role 该资源必须得到角色权限才能访问
         */

        Map<String, String> filterMap = new LinkedHashMap<>();
        //认证拦截
        filterMap.put("/user/add","authc");
        filterMap.put("/user/update","authc");
        //无需认证
        filterMap.put("/test","anon");
        //设置资源授权,需要查看用户是否有对应的权限,如果没有自动跳转到未授权的提示页面,授权的判断是在realm中进行判断
        filterMap.put("/user/add","perms[user:add]"); //设置资源授权的字符是user:add
        filterMap.put("/user/update","perms[user:update]"); //设置资源授权的字符是user:update

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);


        //设置拦截后跳转的登录页面,默认跳转到login.jsp
        shiroFilterFactoryBean.setLoginUrl("/login");
        //设置未授权提示页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauth");

        return shiroFilterFactoryBean;

    }


    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getSecurityManager(@Qualifier("myRealm") MyRealm myRealm){

        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //关联realm
        securityManager.setRealm(myRealm);

        return securityManager;
    }



    @Bean(name = "myRealm")
    public MyRealm getRealm(){
        return new MyRealm();
    }

    /**
     * 配置ShiroDialect 用于thymeleaf 和shiro进行配合使用,可以在页面中使用shiro:标签
     * @return
     */
    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }
}

7. Clase de reino personalizada

继承 AuthorizingRealm
MyRealm

public class MyRealm extends AuthorizingRealm {
    //进行数据库交互
    @Autowired
    private IUserService userService;

    /**
     * 执行授权逻辑
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("授权管理");
        //对资源进行授权
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        //先获取用户登录传递过来的输入,主要是通过下面的重写方法的返回值进行获取
        Subject subject = SecurityUtils.getSubject();
        User user = (User) subject.getPrincipal();

        //通过数据库查看用户的权限字符
        User byId = userService.getById(user.getId());
        info.addStringPermission(byId.getPerms());

        return info;
    }

    /**
     * 执行认证逻辑
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("认证逻辑");
        /**
         * 判断的是shiro的login认证操作,可以自定义或者链接数据库进行查询
         */

        //获取用户输入的username和password
        //请求传递过来的token中包含了所需信息
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;


        QueryWrapper<User> wrapper = new QueryWrapper();
        wrapper.eq("user_name",token.getUsername());
        User one = userService.getOne(wrapper);


        System.out.println(one);
        //判断用户名
        if (one==null){
            //用户名不存在
            return null;
        }
        //判断密码,如果密码正确,将拦截结果交给上面的方法进行资源判断
        return new SimpleAuthenticationInfo(one,one.getPassword(),"");
    }
}

8. desarrollo de páginas html

test.html

<!DOCTYPE html>
<html lang="en" xmlns:shiro="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>test</title>
</head>
<body>
<div shiro:hasPermission="user:add">
    用户新增:<a href="/user/add">用户新增</a>
</div>

<div shiro:hasPermission="user:update" >
    用户修改:<a href="/user/update">用户修改</a>

</div>

</body>
</html>

add.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>test</title>
</head>
<body>

<h1>用户新增</h1>

</body>
</html>

login.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>
<h1>登录界面</h1>
<h2 th:text="${msg}" style="color: red"></h2>
<form method="post" rel="stylesheet" action="/dologin">
    <input type="text" name="name">
    <br>
    <input type="password" name="password">
    <br>
    <input type="submit" value="登录">
</form>
</body>
</html>

unauth.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>test</title>
</head>
<body>

<h1>未授权</h1>

</body>
</html>

update.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>test</title>
</head>
<body>

<h1>用户修改</h1>

</body>
</html>

9. Desarrollo del controlador

TestController

@Controller
public class TestController {
    //初始界面
    @GetMapping("/test")
    public String test(){
        return "test";
    }
    //login界面
    @GetMapping("/login")
    public String login(){
        return "login";
    }
    //dologin
    @PostMapping("dologin")
    public String dologin(String name,String password,Model model) {
        /**
         * 使用shiro进行认证操作
         *
         */
        // 获取subject
        Subject subject = SecurityUtils.getSubject();

        //封装用户数据
        UsernamePasswordToken token = new UsernamePasswordToken(name, password);
        //执行登录方法

        try {
            //表示认证成功
            subject.login(token);
            return "/test";
        } catch (UnknownAccountException e) {
            //表示认证失败,用户不存在
            model.addAttribute("msg", "用户名不存在");
            return "/login";

        } catch (IncorrectCredentialsException e) {
            //表示认证失败,密码错误
            model.addAttribute("msg", "密码错误");
            return "/login";
        }
    }
    //未授权页面
    @GetMapping("/unauth")
    public String unauth(){
        return "unauth";
    }
}

UserController

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

    @GetMapping("/add")
    public String  userAdd(){
        return "add";
    }
    @GetMapping("/update")
    public String  userUpdate(){
        return "update";
    }
}

10. Resultados de la demostración

La idea es: no establezca la interceptación para la página de prueba, pero use el juicio de autoridad de la etiqueta shiro, y la columna de etiqueta correspondiente no se puede ver sin la autoridad.
Inicialmente, la página de prueba se ve directamente sin mostrar ningún contenido.
Inserte la descripción de la imagen aquí

Acceda al inicio de sesión,
Inserte la descripción de la imagen aquí
ingrese el nombre de usuario y la contraseña y consulte dinámicamente la base de datos para el juicio de Shiro
Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí

Después de iniciar sesión correctamente, de acuerdo con el permiso de permisos, la etiqueta específica se mostrará en la página web.
Inserte la descripción de la imagen aquí

Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí

11. Resumen

1. La solicitud del usuario llega al controlador.

  1. Utilice token para encapsular la información del usuario en el método de inicio de sesión del asunto;
  2. Pase el token a Realm para que lo juzgue, use try / catch para juzgar el resultado de la autenticación y procese por separado;

2. Llega al Reino personalizado

  1. Obtenga la información de entrada del usuario a través del token, realice la autenticación y determine si el usuario existe y si la contraseña es correcta;
  2. Si la contraseña del usuario es correcta, realice la gestión de autorizaciones, obtenga la información del usuario a través del asunto, busque la autoridad correspondiente del usuario a través de la base de datos y devuelva la autoridad

3. Alcanza la clase de configuración shiro

  1. Configure para importar su propio Reino e inyectarlo en el contenedor
  2. Configurar DefaultWebSecurityManager y asociar Realm
  3. Configurar ShiroFilterFactoryBean y asociar DefaultWebSecurityManager
  4. Establezca la configuración de interceptación de diferentes solicitudes de página en ShiroFilterFactoryBean, establezca la dirección de solicitud de inicio de sesión que no pasa la verificación después de la intercepción, la predeterminada es login.jsp, y configure la solicitud de página de aviso que ha pasado la verificación pero no está autorizada
  5. Finalmente, configure el soporte de thymeleaf para shiro, introduzca dependencias, configure la clase ShiroDialect y finalmente use etiquetas shiro para controlar la visualización de etiquetas de página en la página.

Supongo que te gusta

Origin blog.csdn.net/Guesshat/article/details/109585273
Recomendado
Clasificación