shiro:集成Springboot(六)

1:导入相关依赖

    <!--thymeleaf 模板引擎依赖包-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    
    <!--web依赖包-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!--shiro整合spring依赖包-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.4.2</version>
    </dependency>

    <!--shiro整合thymeleaf依赖包-->
    <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>
    </dependency>

    <!--druid数据连接池依赖包-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.1.22</version>
    </dependency>

    <!--mybatis整合spring依赖包-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.2</version>
    </dependency>

    <!--mysql驱动依赖包-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>

2:数据库建表以及插入数据

/*用户表*/
CREATE TABLE `t_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(20) NOT NULL,
  `password` varchar(100) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

INSERT INTO `t_user` VALUES ('1', 'songsong', 'IQwrjAZn5lsp6OGpPMT8sOZzNoEPCz4+n6R70xkciM4=', 'bb0491ac-0a93-4edf-8fd7-a295f684b31b');
INSERT INTO `t_user` VALUES ('2', 'yuanhang', 'IQwrjAZn5lsp6OGpPMT8sOZzNoEPCz4+n6R70xkciM4=', 'bb0491ac-0a93-4edf-8fd7-a295f684b31b');
INSERT INTO `t_user` VALUES ('5', 'admin', 'IQwrjAZn5lsp6OGpPMT8sOZzNoEPCz4+n6R70xkciM4=', 'bb0491ac-0a93-4edf-8fd7-a295f684b31b');

/*角色表*/
CREATE TABLE `t_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `role_name` varchar(50) NOT NULL,
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `role_name` (`role_name`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

INSERT INTO `t_role` VALUES ('1', 'banzhang', '2019-10-10 00:00:00');
INSERT INTO `t_role` VALUES ('2', 'student', '2019-10-09 00:00:00');
INSERT INTO `t_role` VALUES ('3', 'user', '2020-04-18 14:52:12');

/*权限表*/
CREATE TABLE `t_permission` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `permission_name` varchar(50) NOT NULL,
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `permission_name` (`permission_name`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;


INSERT INTO `t_permission` VALUES ('1', 'student:yq', '2019-10-09 00:00:00');
INSERT INTO `t_permission` VALUES ('2', 'student:study', '2019-10-09 00:00:00');
INSERT INTO `t_permission` VALUES ('3', 'user:add', '2020-04-18 14:51:36');
INSERT INTO `t_permission` VALUES ('4', 'user:update', '2020-04-18 14:51:47');

/*用户 角色关联表*/
CREATE TABLE `t_user_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL,
  `role_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_id` (`user_id`,`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;


INSERT INTO `t_user_role` VALUES ('1', '1', '1');
INSERT INTO `t_user_role` VALUES ('3', '1', '2');
INSERT INTO `t_user_role` VALUES ('2', '2', '2');
INSERT INTO `t_user_role` VALUES ('4', '5', '3');

/*角色 权限关联表*/
CREATE TABLE `t_role_permission` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `permission_id` int(11) DEFAULT NULL,
  `role_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `permission_id` (`permission_id`,`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

INSERT INTO `t_role_permission` VALUES ('1', '1', '1');
INSERT INTO `t_role_permission` VALUES ('2', '2', '1');
INSERT INTO `t_role_permission` VALUES ('3', '2', '2');
INSERT INTO `t_role_permission` VALUES ('4', '3', '3');
INSERT INTO `t_role_permission` VALUES ('5', '4', '3');

3:构建javaben对象

com\applesnt\shiro\vo\UserVo.java

package com.applesnt.shiro.vo;

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

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserVo {

    //用户id
    private Integer id;
    //用户名称
    private String username;
    //用户密码
    private String password;
}

com\applesnt\shiro\vo\RoleVo.java

package com.applesnt.shiro.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class RoleVo {

    //角色id
    private Integer id;
    //角色名称
    private String roleName;
    //创建时间
    private Date createTime;
}

com\applesnt\shiro\vo\PermissionVo.java

package com.applesnt.shiro.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class PermissionVo {

    //权限id
    private Integer id;
    //权限名称
    private String permissionName;
    //创建时间
    private Date createTime;
}

4:构建mapper接口以及配置文件

com\applesnt\shiro\mapper\UserMapper.java

package com.applesnt.shiro.mapper;

import com.applesnt.shiro.vo.UserVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Component;
import java.util.Set;

//mapper接口要使用@Mapper注解 如果不加 可以再启动类上加上@MapperScan注解
@Mapper
//防止自动注入的时候出红线
@Component
public interface UserMapper {
    //通过用户名查询用户信息
    public UserVo queryUserByUsername(@Param("username") String username);

    //通过用户名查询角色
    public Set<String> queryAllRoleNameByUsername(@Param("username") String username);

    //通过用户名查询权限
    public Set<String> queryAllPermissionByUsername(@Param("username") String username);
}

resources\mybatis\mapper\UserMapper.xml

<?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接口的全路径-->
<mapper namespace="com.applesnt.shiro.mapper.UserMapper">

    <!--通过用户名查询用户信息-->
    <select id="queryUserByUsername" parameterType="string" resultType="UserVo">
        select * from t_user where username = #{username}
    </select>

    <!--通过用户名查询用户角色-->
    <select id="queryAllRoleNameByUsername" parameterType="string" resultType="string">
        SELECT t_role.role_name FROM t_user
        INNER JOIN t_user_role on t_user.id = t_user_role.user_id
        INNER JOIN t_role on t_role.id = t_user_role.role_id
        where t_user.username = #{username}
    </select>

    <!--通过用户名查询用户权限-->
    <select id="queryAllPermissionByUsername" parameterType="string" resultType="string">

        SELECT DISTINCT t_permission.permission_name FROM t_user
        INNER JOIN t_user_role on t_user.id = t_user_role.user_id
        INNER JOIN t_role on t_role.id = t_user_role.role_id
        INNER JOIN t_role_permission on t_role_permission.role_id = t_role.id
        INNER JOIN t_permission on t_permission.id = t_role_permission.permission_id
        where t_user.username = #{username}

    </select>

</mapper>

5:构建mybatis配置文件

resources\mybatis\config\mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>


    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING" /> <!--打印sql语句-->
        <setting name="lazyLoadingEnabled" value="true"/> <!-- 全局性设置懒加载 -->
        <setting name="aggressiveLazyLoading" value="false"/> <!-- 每个属性都按需加载 -->
        <setting name="mapUnderscoreToCamelCase" value="true" /><!-- 开启驼峰命名 -->
    </settings>

</configuration>

6:构建springboot配置文件

resources\application.yml

server:
  port: 80

spring:
  datasource:
    username: root #数据库的用户名
    password: jrfpwd2018 #数据库的用户密码
    url: jdbc:mysql://219.142.105.117:3306/my_test?useUnicode=true&characterEncoding=utf8 # 数据库连接地址
    driver-class-name: com.mysql.cj.jdbc.Driver # 数据库驱动
    type: com.alibaba.druid.pool.DruidDataSource # 数据库类型 使用druid
    druid:
      initial-size: 5 #初始化时建立物理连接的个数(缺省值:0)
      max-active: 200 # 最大连接池数量(缺省值:8)
      min-idle: 1 # 最小链接池数量
      max-wait: 6000 #获取连接时最大等待时间,单位毫秒
      time-between-eviction-runs-millis: 6000
      min-evictable-idle-time-millis: 30000 # 配置一个连接在池中最小生存的时间,单位是毫秒
      validation-query: select 'x' from dual #用来检测连接是否有效的sql,要求是一个查询语
      pool-prepared-statements: false #是否缓存preparedStatement mysql 建议关闭
      test-on-borrow: false #申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
      test-on-return: false # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
      test-while-idle: true # 建议配置为true,不影响性能,并且保证安全性
      connection-init-sqls: select 1 from dual # 物理连接初始化的时候执行的sql
      #监控统计用的filter:stat  日志用的filter:log4j  防御sql注入的filter:wall
      filters: stat,wall,log4j2
      filter:
        stat: # 监控统计
          db-type: mysql
          log-slow-sql: true
          slow-sql-millis: 2000
        wall: #防御sql
          enabled: true
          db-type: mysql
          config:
            delete-allow: true #运行执行删除语句
            drop-table-allow: false #不运行执行删除表语句
        log4j2:
          enabled: true
      stat-view-servlet: #statViewServlet配置
        enabled: true #开启状态
        url-pattern: "/druid/*" #监控页面访问路径
        reset-enable: false # 禁止手动重置监控数据
        login-username: admin # 用户名
        login-password: admin # 密码
      web-stat-filter: #WebStatFilter配置
        enabled: true  #开启状态
        url-pattern: "/*" #拦截请求
        exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*" #排除请求
        session-stat-max-count: 1000 # 监控最大session数,默认是1000
        session-stat-enable: true # 是否启用session的统计功能
        profile-enable: true # 是否启用监控单个Url调用的sql列表
        principal-session-name: session_user_key #使druid当前session的用户是谁
        principal-cookie-name: cookie_user_key #使druid当前user的用户是谁
#mybatis配置
mybatis:
  config-location: classpath:mybatis/config/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml
  type-aliases-package: com.applesnt.shiro.vo

7:构建service接口以及实现类

接口:com\applesnt\shiro\service\UserService.java

package com.applesnt.shiro.service;

import com.applesnt.shiro.vo.UserVo;
import org.apache.ibatis.annotations.Param;
import java.util.Set;

public interface UserService {

    //通过用户名查询用户信息
    public UserVo queryUserByUsername(@Param("username") String username);

    //通过用户名查询角色
    public Set<String> queryAllRoleNameByUsername(@Param("username") String username);

    //通过用户名查询权限
    public Set<String> queryAllPermissionByUsername(@Param("username") String username);
}

实现类:com\applesnt\shiro\service\impl\UserServiceImpl.java

package com.applesnt.shiro.service.impl;

import com.applesnt.shiro.mapper.UserMapper;
import com.applesnt.shiro.service.UserService;
import com.applesnt.shiro.vo.UserVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Set;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    UserMapper userMapper;

    @Override
    public UserVo queryUserByUsername(String username) {
        return this.userMapper.queryUserByUsername(username);
    }

    @Override
    public Set<String> queryAllRoleNameByUsername(String username) {
        return this.userMapper.queryAllRoleNameByUsername(username);
    }

    @Override
    public Set<String> queryAllPermissionByUsername(String username) {
        return this.userMapper.queryAllPermissionByUsername(username);
    }
}

8:构建自定义shiro的Realm

com\applesnt\shiro\config\MyRealm.java

package com.applesnt.shiro.config;

import com.applesnt.shiro.service.UserService;
import com.applesnt.shiro.vo.UserVo;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Set;

public class MyRealm extends AuthorizingRealm {

    @Autowired
    UserService userService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {

        System.out.println("权限检查");

        //获取用户登录时发送过来的用户名
        String username = principal.getPrimaryPrincipal().toString();
        System.out.println(username);
        Set<String> roles = userService.queryAllRoleNameByUsername(username);
        Set<String> perms = userService.queryAllPermissionByUsername(username);

        /*设置角色*/
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
        /*设置权限*/
        info.setStringPermissions(perms);
        
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("登录检查");
        //获取用户登录时发送过来的用户名
        String username = token.getPrincipal().toString();

        UserVo userVo = userService.queryUserByUsername(username);
        if(userVo==null){
            return null;
        }

        return new SimpleAuthenticationInfo(userVo.getUsername(),
                userVo.getPassword(),
                ByteSource.Util.bytes(userVo.getSalt()), //盐
                this.getName());
    }
}

9:构建shiro配置类

com\applesnt\shiro\config\ShiroConfig.java

package com.applesnt.shiro.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
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 {

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

        //添加shiro的内置过滤器规则 anon authc perms role
        Map<String,String> filterMap = new LinkedHashMap<>();
        filterMap.put("/user/update","perms[user:update]");
        filterMap.put("/user/add","authc");

        shiroFilter.setFilterChainDefinitionMap(filterMap);
        /*未登录的跳转页面*/
        shiroFilter.setLoginUrl("/toLogin");
        /*无角色权限认证的跳转页面*/
        shiroFilter.setUnauthorizedUrl("/noperms");
        return shiroFilter;
    }

    /*声明安全管理器,要注册到shiroFilter中*/
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("myRealm") MyRealm myRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myRealm);
        return securityManager;
    }

    /*声明自定义realm 连接数据,要注册到管理器中*/
    @Bean
    public MyRealm myRealm(HashedCredentialsMatcher hashedCredentialsMatcher){
        MyRealm myRealm = new MyRealm();
        /*注册密码管理器*/
        myRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        return myRealm;
    }

    /*声明密码管理器,要在自定义realm中注册*/
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 使用SHA 算法进行加密
        hashedCredentialsMatcher.setHashAlgorithmName("SHA-256");
        hashedCredentialsMatcher.setStoredCredentialsHexEncoded(false);
        // 设置散列次数: 意为加密几次
        hashedCredentialsMatcher.setHashIterations(10000);

        return hashedCredentialsMatcher;
    }

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

10:构建controller控制类

com\applesnt\shiro\controller\MyController.java

package com.applesnt.shiro.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class MyController {

    /*首页请求*/
    @RequestMapping(value = {"/","index"})
    public String index(Model model){
        model.addAttribute("msg","hello shiro");
        return "index";
    }

    /*模拟添加用户请求*/
    @RequestMapping("/user/add")
    public String add(Model model){
        return "user/add";
    }

    /*模拟修改用户请求*/
    @RequestMapping("/user/update")
    public String update(Model model){
        return "user/update";
    }

    /*跳转登录页面*/
    @RequestMapping("/toLogin")
    public String toLogin(){
        return "login";
    }

    /*无权限提示页面*/
    @RequestMapping("/noperms")
    @ResponseBody
    public String noperms(){
        return "未授权页面";
    }

    /*登录请求*/
    @RequestMapping("/login")
    public String login(String username,String password,Model model){
        //获取subject
        Subject subject = SecurityUtils.getSubject();
        //获取令牌 自动调用自定义的realm进行身份认证
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        //记住我的设置
        token.setRememberMe(true);
        try{
            //身份认证
            subject.login(token);
            System.out.println("登录名为:" + subject.getPrincipal());
            return "welcome"; //登录成功
        }catch(UnknownAccountException e){
            model.addAttribute("errmsg","用户名错误");
            return "login";
        }catch(IncorrectCredentialsException e){
            model.addAttribute("errmsg","密码错误");
            return "login";
        }

    }
}

11:构建相关界面

说明:shiro标签在模板中的用法与jsp一致,命名空间需要改一下
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"

resources\templates\index.html(首页)

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h3 th:text="首页"></h3>

<p th:text="${msg}"></p>

<a th:href="@{/toLogin}">去登录</a>

</body>
</html>

resources\templates\login.html(登录页)

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h2>登录</h2>
<p th:text="${errmsg}"></p>
<form th:action="@{/login}" method="post">
    用户名:<input type="text" name="username"><br>
    密  码:<input type="text" name="password"><br>
    <input type="submit" value="提交">
</form>
</body>
</html>

resources\templates\welcome.html(欢迎页)

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<shiro:authenticated>
    欢迎您:<shiro:principal />
</shiro:authenticated>

<p th:text="${msg}"></p>

<br/>
<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>

</body>
</html>

resources\templates\user\add.html(模拟用户添加页)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>add页面</h1>
</body>
</html>

resources\templates\user\update.html(模拟用户添加页)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>add页面</h1>
</body>
</html>

12:访问测试

说明:(参考数据库)
用户名admin  密码:123 权限只有add
用户名yuanhang 密码:123 无add update权限

posted @ 2020-04-24 22:34  努力的校长  阅读(264)  评论(0编辑  收藏  举报