Spring Boot & Shiro集成

Scroll Down

Spring Boot & Shiro集成

导入 maven 依赖

导入spring-maven依赖

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

其他spring boot依赖包括

  1. mybatis相关,数据库相关
  2. thymeleaf相关

数据库表的建立

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `userId` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) NOT NULL,
  `password` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`userId`),
  UNIQUE KEY `user_username_uindex` (`username`),
  UNIQUE KEY `user_password_uindex` (`password`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

简单的user表,后面功能可以再进行添加

编写获取账号密码的DAO层

我这里使用了mybatis,mybatis的配置我也放到这里了

mybatis配置

mybatis:
  type-aliases-package: com.lucas.springbootlearn.pojo
  mapper-locations: classpath:mappers/*.xml

druid配置

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    password: root
    username: root
    url: jdbc:mysql://localhost:3306/web?serverTimezone=UTC
    dbType: mysql   # 指定数据库类型 mysql
    initialSize: 5  # 启动初始化连接数量
    minIdle: 5      # 最小空闲连接
    maxActive: 20   # 最大连接数量(包含使用中的和空闲的)
    maxWait: 60000  # 最大连接等待时间 ,超出时间报错
    type: com.alibaba.druid.pool.DruidDataSource
@Configuration
public class DruidConfig {

    //注册bean
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource(){
        return  new DruidDataSource();
    }
    
}

编写mapper

<mapper namespace="com.lucas.springbootlearn.dao.UserMapper">
   <select id="findUserByName" resultType="user">
        select * from user where username = '${username}'
    </select>
</mapper>

编写DAO

@Mapper
@Repository
public interface UserMapper {
    public User findUserByName(@Param("username") String username);
}

编写Shiro

编写Shiro配置类

shiro需要三样东西

  1. ShiroFilterFactoryBean
  2. DefaultWebSecurityManager
  3. Realm对象
@Configuration
public class ShiroConfig {
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean()
    {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        
        //代码
        return shiroFilterFactoryBean;
    }
    
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager()
    {
        DefaultWebSecurityManager defaultWebSecurityManager = 
            new DefaultWebSecurityManager();
		//代码
        return defaultWebSecurityManager;
    }
    
    @Bean
    public Realm realm()
    {
        //代码
    }
    
}

大致框架就是这样

编写realm

我们固然可以使用JdbcRealm

但是我们也可以编写自定义的Realm

我们新建一个Realm叫MyRealm

自定义的Realm集成AuthorizingRealm毕竟方便

public class MyRealm extends AuthorizingRealm {

    @Autowired
    private UserMapper mapper;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //授权信息

        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //登录验证的信息
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;

        //我们只需要根据用户名查询数据库
        User user = mapper.findUserByName(
                new String(usernamePasswordToken.getUsername())
        );

        //return null 会抛出一个用户名不存在异常
        //我们这边只去实现登录的功能
        if (user==null)
            return null;

        // shiro 执行比对密码的功能
        return new SimpleAuthenticationInfo("",user.getPassword(),"");
    }
}

授权部分暂时不写,只写了登录验证部分

  • AuthenticationToken 是Controller层传入的Token
  • 我们通过查询数据库获取正确的密码
  • 我们判断是不是空的,如果为空进行处理
  • 我们把比对密码的事情交给Shiro

简单的Controller编写

@RequestMapping("/userLogin")
public String login(User user, Model model, HttpSession session)
{
    UsernamePasswordToken usernamePasswordToken = 
        new UsernamePasswordToken(user.getName(), user.getPassword());
    Subject subject = SecurityUtils.getSubject();
    try {
        subject.login(usernamePasswordToken);
        session.setAttribute("user",user);
    }catch (UnknownAccountException uae) {
        System.out.println("用户名不存在");
        return "/login";
    } catch (IncorrectCredentialsException ice) {
        System.out.println("密码错误");
        return "/login";
    }
    //权限管理这一部分还要继续添加
    return "adminIndex";
}

稍微总结一下,Controller的编写分为这几步

  • 获取表单提交的数据
  • 封装成一个User对象
  • 获取Subject
  • 获取Token
  • 验证登录/捕获异常
  • 对异常进行处理

回到Config & 编写拦截器

@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager)
{
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);

    // 添加过滤器
    /*
     * 1. anon: 无需认证就能访问
     * 2. authc: 需要认证才能用
     * 3. user: 记住我功能开启才能用
     * 4. perms: 拥有权限才能访问
     * 5. roles: 拥有某个角色权限才能访问
     */
    HashMap<String, String> map = new LinkedHashMap<>();
    //首页所有人可以访问
    map.put("/allBook","anon");
    map.put("/login","anon");
    map.put("/","anon");
    map.put("/logout","anon");


    //其他管理员界面都要登录
    map.put("/user/*","authc");
    shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
    shiroFilterFactoryBean.setLoginUrl("/login");
    return shiroFilterFactoryBean;
}

@Bean(name = "defaultWebSecurityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("realm") MyRealm realm)
{
    DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
    //关联 Realm
    defaultWebSecurityManager.setRealm(realm);
    return defaultWebSecurityManager;
}

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

这里值得注意的是过滤器的配置

  1. anon: 无需认证就能访问
  2. authc: 需要认证才能用
  3. user: 记住我功能开启才能用
  4. perms: 拥有权限才能访问
  5. roles: 拥有某个角色权限才能访问

在这里还能配置更多的Shiro内容

用户权限的认证

在MyRealm中编写

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    //授权信息
    SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

    //这里给每个用户都添加了以下权限
    simpleAuthorizationInfo.addStringPermission("admin.*");
    return simpleAuthorizationInfo;
}

Config中编写权限要求

//其他管理员界面都要登录
map.put("/user/*","perms[admin:user]");
map.put("/book/*","perms[admin:book]");
map.put("/management/**","perms[admin:book]");

实际上在Realm中的权限添加是要去数据库中寻找的,这里比较方便

shiro整合thymeleaf

shiro整合thymeleaf也比较简单

增加以下依赖

<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>

在配置中要配置Bean

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

标签和jsp中的标签很像

参考

https://blog.csdn.net/qq_34579313/article/details/82024058