SpringBoot学习---SpringSecurity与Shiro

视频学习链接:
在这里插入图片描述
狂神说SpringBoot18:集成SpringSecurity

一.SpringSecurity

1.什么是SpringSecurity

Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它实际上是保护基于spring的应用程序的标准。

2.实际操作

需求:根据用户的权限,限制用户可进入的页面,如用户权限为VIP1可以访问/level1/下的所有页面,用户权限为VIP2可以访问/level2/下的所有页面,用户权限为VIP3可以访问/level3/下的所有页面。
在这里插入图片描述

(1) 项目框架

在这里插入图片描述

(2) 项目使用的依赖(在pom.xml中)
        <!-- 导入thymeleaf模板引擎-->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
        </dependency>

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

        <!--thymeleaf与security的整合-->
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        </dependency>
(3) 前端界面

在这里插入图片描述
Login.html:用户登入页面

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/index" method="post">
    <p>账号:</p><input type="text" placeholder="请输入账号" name="userName">
    <p>注销:</p><input type="password" placeholder="请输入密码" name="passWord">
    </br>
    <input type="checkbox" name="remember">记住我
    </br>
    <input type="submit" value="登入">
</form>
</body>
</html>

index.html:网站主界面

<!DOCTYPE html>
<html lang="en" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<H1>首页</H1>
<div class="right menu">
    <!--如果未登入-->
    <div sec:authorize="!isAuthenticated()">
        <a th:href="@{/tologin}" class="item"/>登入
    </div>

    <div sec:authorize="isAuthenticated()">
        <a class="item">
            用户名: <span sec:authentication="name"></span>
            角色:<span sec:authentication="principal.authorities"></span>
        </a>
    </div>

    <div sec:authorize="isAuthenticated()">
        <a th:href="@{/logout}"  class="item"/>注销
    </div>
</div>

<div sec:authorize="hasRole('VIP1')">
    <h2>level1</h2>
    <a href="/level1/1">level1-1</a>
    <a href="/level1/2">level1-2</a>
    <a href="/level1/3">level1-3</a>
</div>

<div sec:authorize="hasRole('VIP2')">
    <h2>level2</h2>
    <a href="/level2/1">level2-1</a>
    <a href="/level2/2">level2-2</a>
    <a href="/level2/3">level2-3</a>
</div>

<div sec:authorize="hasRole('VIP3')">
    <h2>level3</h2>
    <a href="/level3/1">level3-1</a>
    <a href="/level3/2">level3-2</a>
    <a href="/level3/3">level3-3</a>
</div>

</body>
</html>

/leve1/X.html随便写写即可
在这里插入图片描述

(4) controller层实现页面的跳转

SecurityProjectController类:
在这里插入图片描述

(5) SpringSecurity的使用

经过前面操作,基本上可以实现界面的跳转,但是还无法实现根据用户权限的不同,展示不同的页面,这个时候我们需要使用SpringSecurity。

  • 导入SpringSecurity的依赖
    在这里插入图片描述
  • 在config中创建SecurityConfig类(自定义Spring Security 配置类)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    
    // 授权
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    
    
        // 定制请求的授权规则
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/level1/**").hasRole("VIP1")
                .antMatchers("/level2/**").hasRole("VIP2")
                .antMatchers("/level3/**").hasRole("VIP3");
        // 开启登入
        http.formLogin().loginPage("/tologin").usernameParameter("userName").passwordParameter("passWord").loginProcessingUrl("/index");
       // 注销
        http.logout().logoutSuccessUrl("/tologin");
        http.csrf().disable();
        // 开启记住我功能
        http.rememberMe().rememberMeParameter("remember");
    }


    // 认证
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
    
       // 从内存中定义
        auth.inMemoryAuthentication()
                .passwordEncoder(new BCryptPasswordEncoder())
                .withUser("admin").password(new BCryptPasswordEncoder().encode("123456")).roles("VIP1")
                .and()
                .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("VIP2")
                .and()
                .withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("VIP3");
    }
}

WebSecurityConfigurerAdapter:自定义Security策略(自定义Spring Security 配置类继承该类即可)
在这里插入图片描述

@EnableWebSecurity:开启WebSecurity模式
在这里插入图片描述

AuthenticationManagerBuilder:自定义认证策略
在这里插入图片描述
Spring Security的两个主要目标是 “认证” 和 “授权”(访问控制):
重写configure(HttpSecurity http)方法用于授权
在这里插入图片描述
重写configure(AuthenticationManagerBuilder auth)方法用于认证
在这里插入图片描述

(6) 运行效果

在这里插入图片描述
这个登入界面是使用自己编写的(SpringSecurity本身提供登入界面)
在这里插入图片描述
登入admin(权限为VIP1)

在这里插入图片描述
在这里插入图片描述
登入root(权限为VIP2)
在这里插入图片描述
登入guest用户(权限为VIP3)
在这里插入图片描述

二.Apache Shiro

1.什么是Apache Shiro

Apache Shiro是一个功能强大且易于使用的Java安全框架,提供了认证,授权,加密,和会话管理。如同 Spring security 一样都是是一个权限安全框架,但是与Spring Security相比,在于他使用了和比较简洁易懂的认证和授权方式。

2.三大核心组件

  • subject:主体,可以是用户也可以是程序,主体要访问系统,系统需要对主体进行认证、授权。
  • securityManager: 安全管理器,主体进行认证和授权都是通过它进行。
  • realm:领域,可以理解为DAO,通过它存取认证、授权相关数据。

3.四大核心功能

  • Authentication:认证即登录,用于用户身份识别。
  • Authorization: 授权即访问控制,判断用户是否有权限去访问受保护的资源。
  • Cryptography: 通过加密算法保护数据安全。
  • Session Management:会话管理,即用户访问你应用自身携带的数据,甚至可以在非Web程序

4.实际操作

需求:通过查询用户的perms字段的值,如果为user:add表示该用户可以进入add.html;如果为user:update表示该用户进入update.html
在这里插入图片描述

(1) 项目结构

在这里插入图片描述

(2) 导入依赖(在pom.xml中)
        <!--导入shiro依赖-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.1</version>
        </dependency>

        <!--数据库-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.19</version>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.12</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>

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

        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
(3) 前端界面

在这里插入图片描述
index.html:网站首页界面

<!DOCTYPE html>
<html lang="en"
      xmlns:th="https://www.thymeleaf.org"
      xmlns:shiro="https://shiro.apache.org/jsp-tag-library.html">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<h1>首页</h1>
<p th:text="${msg}" style="color: red;"></p>

<shiro:notAuthenticated>
  <a th:href="@{/toLogin}">登入</a>
</shiro:notAuthenticated>

<shiro:authenticated>  
  
  <shiro:haspermission name="user:add">
    <a th:href="@{/user/add}">add</a>
  </shiro:haspermission>


  <shiro:haspermission name="user:update">
    <a th:href="@{/user/update}">update</a>
  </shiro:haspermission>

</shiro:authenticated>

</body>
</html>



login.html:用户登入界面

<!DOCTYPE html>
<html lang="en"  xmlns:shiro="https://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
    <meta charset="UTF-8">
    <title>登入界面</title>
</head>
<body>
<form th:action="@{/submitLogin}" method="post">
    <p th:text="${msg}" style="color: red;"></p>
   账号:<input type="text" name="userName">
  密码:<input type="password" name="passWord">
   <input type="submit" value="登入">
</form>
</body>
</html>

User下的add.html和update.html随便写写即可:
在这里插入图片描述

(4) controller层实现页面的跳转
@Controller
public class MyController {
    
    
    @RequestMapping({
    
    "/","/index"})
    public String toIndex(Model model){
    
    
        model.addAttribute("msg","hello,shiro");
        return "index";
    }

    @RequestMapping("/user/add")
    public String add(){
    
    
        return "/User/add";
    }

    @RequestMapping("/user/update")
    public String update(){
    
    
        return "/User/update";
    }

    @RequestMapping("/toLogin")
    public String toLogin(){
    
    
        return "login";
    }

    @RequestMapping("/submitLogin")
    public String login(@RequestParam("userName") String username, @RequestParam("passWord")String passWord, Model model){
    
    
        // 获取当前对象
        Subject subject = SecurityUtils.getSubject();
        // 封装用户的登入数据
        UsernamePasswordToken token =new UsernamePasswordToken(username,passWord);
        System.out.println("进行登入!");
        try {
    
    
            subject.login(token);// 执行登入方法
            return "index";
        }catch (UnknownAccountException e){
    
    
            model.addAttribute("msg","用户名错误");
            return "login";
        }catch (IncorrectCredentialsException e){
    
    
            model.addAttribute("msg","密码错误");
            return "login";
        }
    }


    @ResponseBody
    @RequestMapping("/auth")
    public String noauth(){
    
    
        return  "没有授权!";
    }


}

这里注意登入使用的Subject对象进行登入:
在这里插入图片描述

(5) Dao层用户的数据从数据库中获取

数据表结构:
在这里插入图片描述
数据:
在这里插入图片描述
数据源配置application.yaml中:

spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver

pojo:
在这里插入图片描述

Mapper层:
在这里插入图片描述
在这里插入图片描述
不要忘了扫描一下包:
application.properties中
在这里插入图片描述
由于映射文件UserMapper.xml放在Java文件中(资源过滤),不然无法生成该UserMapper.xml:

        <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>

在这里插入图片描述

(6) Service层

UserService接口:
在这里插入图片描述
UserServiceImpl实现类:
在这里插入图片描述

(7) Shiro的使用
  • ShiroConfig配置

三个bean对象:(userRealm,DefaultWebSecurityManager,ShiroFilterFactoryBean)

@Configuration
public class ShiroConfig {
    
    
    // ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
    
    
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        // 设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);
        // 添加shiro的内置过滤器
        Map<String,String> filterMap =new LinkedHashMap<>();
        filterMap.put("/user/add","perms[user:add]");
        filterMap.put("/user/update","perms[user:update]");
        filterMap.put("/user/*","authc");
        bean.setFilterChainDefinitionMap(filterMap);
        // 设置登入页面
        bean.setLoginUrl("/toLogin");
        bean.setUnauthorizedUrl("/auth");
        return bean;
    }

    // DefaultWebSecurityManager
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
    
    
        // 关联UserRealm
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        return securityManager;
    }


    // 创建realm对象
    @Bean
    public UserRealm userRealm() {
    
    
        return new UserRealm();
    }

    @Bean
    public ShiroDialect shiroDialect(){
    
    
        return new ShiroDialect();
    }
}


  • 授权与认证userService类
public class UserRealm extends AuthorizingRealm {
    
    
    @Autowired
    private UserServiceImpl userService;
    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    
    
       System.out.println("执行了=>授权doGetAuthorizationInfo");
       // 进行授权
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 通过数据库授权
        Subject subject = SecurityUtils.getSubject();
        User user = (User) subject.getPrincipal();
        info.addStringPermission(user.getPerms());
        return info;
    }

    // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
    
        System.out.println("执行了=>验证doGetAuthenticationInfo");
//        // 获取用户名,密码
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        User user = userService.getUserByName(userToken.getUsername());
        if (user == null){
    
    
            return null;
        }

        // 密码验证
        return new SimpleAuthenticationInfo(user,user.getPwd(),"");
    }
}

(8) 运行效果

在这里插入图片描述

登入admin用户(只有add权限):
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

登入测试(只有update权限):

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_42753193/article/details/123765194