SpringBoot intègre le code de cas complet Shiro

SpringBoot intègre le code de cas complet Shiro

1. API principale de Shiro

Objet Principal de l'utilisateur
SecurityManager Security manager
Realm shiro connection data bridge

2. Structure du projet et structure de la base de données

Insérez la description de l'image ici
Insérez la description de l'image ici

3. Importation de dépendances

        <!--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. Utilisez le générateur de code générateur

Générateur de code

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();
    }
}

Modifiez la configuration de la connexion à la base de données et l'adresse de stockage de la génération de fichier

5. Classe d'entité générée, objet mappeur, service

Utilisateur

@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. Configuration de l'application

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. classe de configuration 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. Classe de royaume personnalisée

继承 AutoriserRealm
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. Développement de pages 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. Développement du contrôleur

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. Résultats de la démonstration

L'idée est la suivante: ne définissez pas l'interception pour la page de test, mais utilisez le jugement d'autorité de la balise shiro, et la colonne de balise correspondante ne peut pas être visualisée sans l'autorisation.
Initialement, la page de test est directement visualisée sans afficher de contenu.
Insérez la description de l'image ici

Accédez à la connexion,
Insérez la description de l'image ici
entrez le nom d'utilisateur et le mot de passe et interrogez dynamiquement la base de données pour obtenir un jugement shiro
Insérez la description de l'image ici
Insérez la description de l'image ici

Après une connexion réussie, selon l'autorisation perms, la balise spécifique sera affichée sur la page Web.
Insérez la description de l'image ici

Insérez la description de l'image ici
Insérez la description de l'image ici

11. Résumé

1. La demande de l'utilisateur arrive au contrôleur

  1. Utilisez un jeton pour encapsuler les informations utilisateur dans la méthode de connexion du sujet;
  2. Passez le jeton à Realm pour jugement, utilisez try / catch pour juger du résultat de l'authentification et traitez-les séparément;

2. Atteignez le royaume personnalisé

  1. Obtenez les informations d'entrée de l'utilisateur via le jeton, effectuez l'authentification et déterminez si l'utilisateur existe et si le mot de passe est correct;
  2. Si le mot de passe de l'utilisateur est correct, effectuez la gestion des autorisations, obtenez des informations sur l'utilisateur via le sujet, recherchez l'autorité correspondante de l'utilisateur via la base de données et renvoyez l'autorité.

3. Atteignez la classe de configuration shiro

  1. Configurer pour importer votre propre royaume et l'injecter dans le conteneur
  2. Configurer DefaultWebSecurityManager et associer Realm
  3. Configurer ShiroFilterFactoryBean et associer DefaultWebSecurityManager
  4. Définissez les paramètres d'interception des différentes demandes de page dans ShiroFilterFactoryBean, définissez l'adresse de demande de connexion qui ne réussit pas la vérification après l'interception, la valeur par défaut est login.jsp et définissez la demande de page d'invite qui a réussi la vérification mais n'est pas autorisée
  5. Enfin, configurez le support de thymeleaf pour shiro, introduisez les dépendances, configurez la classe ShiroDialect, et enfin utilisez les balises shiro pour contrôler l'affichage des balises de page dans la page

Je suppose que tu aimes

Origine blog.csdn.net/Guesshat/article/details/109585273
conseillé
Classement