SpringBoot--Shiro

Shiro

什么是Shiro?

  • Apache Shiro是一个Java的安全(权限)框架。
  • Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE环境,也可以用在JavaEE环境。
  • Shiro可以完成, 认证,授权,加密,会话管理,Web集成,缓存等。

image-20210825094851180

1、pom.xml

 <dependencies>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.5.3</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>

2、log4j.properties

log4j.rootLogger=INFO, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n

# General Apache libraries
log4j.logger.org.apache=WARN

# Spring
log4j.logger.org.springframework=WARN

# Default Shiro logging
log4j.logger.org.apache.shiro=INFO

# Disable verbose logging
log4j.logger.org.apache.shiro.util.ThreadContext=WARN
log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN

3、引入shiro.inn和Quickstart.java 文件

常用方法:

//获取当前的用户对象  Subject
        Subject currentUser = SecurityUtils.getSubject();
//通过当前用户拿到session
        Session session = currentUser.getSession();
//测试当前的用户是否被认证
        currentUser.isAuthenticated();
currentUser.getPrincipal()//their identifying principal
currentUser.login(token);  //执行登录操作
//test a role:
  		currentUser.hasRole("schwartz");
//test a typed permission (not instance-level)
        //粗粒度
        currentUser.isPermitted("lightsaber:wield")
//a (very powerful) Instance Level permission:
        //细粒度
        currentUser.isPermitted("winnebago:drive:eagle5")          
 //注销
        currentUser.logout();
            
            

Springboot集成shiro

环境搭建

1、pom.xml 导入shiro和spring的jar包

 <!--Thymeleaf-->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-java8time</artifactId>
        </dependency>
 <!--shiro整合spring的包-->
        <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>

命名空间:

 xmlns:th="http://www.thymeleaf.org"

Subject —用户

Security- —管理所有用户

Realm—连接数据

2、配置文件(UserRealm)编写

image-20210825120410466

1、 UserRealm

package com.kuang.config;


import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

//自定义的UserRealm  需要继承   AuthorizingRealm
public class UserRealm extends AuthorizingRealm {

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了=》授权");
        return null;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行了=》认证");
        return null;
    }
}

2、ShiroConfig

package com.kuang.config;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


/*倒着配*/
@Configuration
public class ShiroConfig {

    //ShiroFilterFactoryBean    3
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);
        return bean;
    }

    //DafaultWebSecurityManager ,从spring中取得UserRealm,将二者关联起来    2
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
        DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
        //关联UserRealm
        securityManager.setRealm(userRealm);

        return securityManager;
    }

    //创建 realm 对象,需要自定义类
    //将一个类bean注入到Configuration里面, 被spring接管   1
    @Bean
    public UserRealm userRealm(){
        return new UserRealm();
    }

}

实现登录拦截

拦截请求在ShiroConfig配置类中实现

//添加shiro的内置过滤器
/*
* anno: 无需验证就可以访问
* authc: 必须认证了才能访问
* user: 必须开启记住我 功能才能访问
* perms:拥有对某个资源的权限才能访问
* role: 拥有某个角色权限才能访问
*  */
 //ShiroFilterFactoryBean    3
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);

        //添加shiro的内置过滤器
        /*
        * anno: 无需验证就可以访问
        * authc: 必须认证了才能访问
        * user: 必须开启记住我 功能才能访问
        * perms:拥有对某个资源的权限才能访问
        * role: 拥有某个角色权限才能访问
        *  */
        Map<String,String> filterMap=new LinkedHashMap<>();

        filterMap.put("/user/add","authc");
        filterMap.put("/user/update","authc");

        bean.setFilterChainDefinitionMap(filterMap);

        //设置登录的请求
        bean.setLoginUrl("/toLogin");

        return bean;
    }
Shiro实现用户认证

认证在UserRealm类中实现

1、login.html

image-20210825153431451

1、MyController

//登录表单提交到这里
    @RequestMapping("/login")
    public String login(String username,String password,Model model){
        //获得当前的用户
        Subject currentUser = SecurityUtils.getSubject();
        //封装用户的登录数据
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);


        try{
            currentUser.login(token);  //执行登陆方法,如果没有异常就说明ok了
            return "index";

        } catch (UnknownAccountException e) { //用户名不存在
            model.addAttribute("msg","用户名错误");
            return "login";
        }catch (IncorrectCredentialsException e){
            model.addAttribute("msg","密码错误"); //密码不存在
            return "login";
        }

    }

2、UserRealm

 //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行了=》认证");

        //用户名,密码  数据库中取
        String name="root";
        String password="111";

        UsernamePasswordToken userToken=(UsernamePasswordToken)token;

        if (!userToken.getUsername().equals(name)){
            return null;  //抛出异常  UnknownAccountException
        }

        //密码认证,shiro做了
        return new SimpleAuthenticationInfo("",password,"");
    }
shiro整合mybatis

1、pom.xml

<dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.21</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/log4j/log4j -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
  <!--引入mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>
  <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

2、配置文件

image-20210825160722186

application.yaml

spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/springboot?serverTimezone=GMT&useSSL=false
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    #url: jdbc:mysql://localhost:3306/springboot?serverTimezone=GMT

    #Spring Boot 默认是不注入这些属性值的,需要自己绑定
    #druid 数据源专有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

    #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
    #如果允许时报错  java.lang.ClassNotFoundException: org.apache.log4j.Priority
    #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
    filters: stat,wall,log4j
    #连接失败后重试次数
    connection-error-retry-attempts: 5
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

application.properties

mybatis.type-aliases-package=com.kuang.pojo
mybatis.mapper-locations=classpath:mapper/*.xml

3、user实体类、UserMapper接口书写,Usermapper.xml书写

package com.kuang.pojo;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String username;
    private String password;
}
package com.kuang.mapper;

import com.kuang.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

@Mapper   //这个注解表示这是一个mybatis的mapper类
@Repository //标明是dao
public interface UserMapper {

     User queryUserByName(String name);
}

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kuang.mapper.UserMapper">
    <select id="queryUserByName" resultType="User">
    select * from user where username=#{name};
  </select>
</mapper>

改造之前的UserRealm配置文件连接数据库

 //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行了=》认证");

        UsernamePasswordToken userToken=(UsernamePasswordToken)token;
        //用户名,密码  数据库中取
        User user = userService.queryUserByName(userToken.getUsername());

        if (user==null){
            return null;  //抛出异常  UnknownAccountException
        }
        //密码认证,shiro做了
        return new SimpleAuthenticationInfo("",user.getPassword(),"");
    }
Shiro请求授权实现
  @RequestMapping("/noauch")
    @ResponseBody
    public String auth(){
        return "没有权限访问";
    }

授权

获取当前用户权限

//授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了=》授权");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        //拿到当前登录用户
        Subject subject = SecurityUtils.getSubject();
        User currentUser= (User) subject.getPrincipal();

        //设置当前用户的权限
        info.addStringPermission(currentUser.getPerms());

     
        return info;
    }

设置链接权限


        //授权,指定该请求需要有该权限才能访问,    没有权限的话会跳转到未授权页面
        filterMap.put("/user/add","perms[user:add]");  //add需要有user:add权限
        filterMap.put("/user/update","perms[user:update]");  //update需要有user:update

image-20210825174031895

修改表结构

image-20210825173404144

实体表也要改

注意:

image-20210825173516409
想实现用户登录后只显示他拥有权限的链接,需要shiro整合thymeleaf

1、导入jar包

  <!--shiro整合thymeleof-->
        <!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>


xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"

2、配置


    //整合thymeleaf
    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }

image-20210825191225049

shiro和thymeleaf 命名空间:xmlns:shiro=“http://www.pollix.at/thymeleaf/shiro”

image-20210825191509207

<div shiro:notAuthenticated></div> //表示没有身份验证时显示信息

完整的ShiroConfig

package com.kuang.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;


/*倒着配*/
@Configuration
public class ShiroConfig {

    //ShiroFilterFactoryBean    3
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);

        //添加shiro的内置过滤器
        /*
        * anno: 无需验证就可以访问
        * authc: 必须认证了才能访问
        * user: 必须开启记住我 功能才能访问
        * perms:拥有对某个资源的权限才能访问
        * role: 拥有某个角色权限才能访问
        *  */
        //拦截
        Map<String,String> filterMap=new LinkedHashMap<>();

        //授权,指定该请求需要有该权限才能访问,    没有权限的话会跳转到未授权页面
        filterMap.put("/user/add","perms[user:add]");  //add需要有user:add权限
        filterMap.put("/user/update","perms[user:update]");  //update需要有user:update

        filterMap.put("/user/*","authc");
        bean.setFilterChainDefinitionMap(filterMap);

        //设置登录的请求
        bean.setLoginUrl("/toLogin");
        //设置为没有权限的页面
        bean.setUnauthorizedUrl("/noauch");

        return bean;
    }

    //DafaultWebSecurityManager ,从spring中取得UserRealm,将二者关联起来    2
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
        DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
        //关联UserRealm
        securityManager.setRealm(userRealm);

        return securityManager;
    }

    //创建 realm 对象,需要自定义类
    //将一个类bean注入到Configuration里面, 被spring接管   1
    @Bean
    public UserRealm userRealm(){
        return new UserRealm();
    }


    //整合thymeleaf
    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }
}


完整的UserRealm

package com.kuang.config;


import com.kuang.pojo.User;
import com.kuang.service.UserService;
import com.kuang.service.UserServiceImpl;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
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.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;

//自定义的UserRealm  需要继承   AuthorizingRealm
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了=》授权");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        //拿到当前登录用户
        Subject subject = SecurityUtils.getSubject();
        User currentUser= (User) subject.getPrincipal();

        //设置当前用户的权限
        info.addStringPermission(currentUser.getPerms());

        return info;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行了=》认证");

        UsernamePasswordToken userToken=(UsernamePasswordToken)token;
        //用户名,密码  数据库中取
        User user = userService.queryUserByName(userToken.getUsername());

        if (user==null){
            return null;  //抛出异常  UnknownAccountException
        }
        //密码认证,shiro做了
        return new SimpleAuthenticationInfo(user,user.getPassword(),"");
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值