Springboot:自定义Realm

1、导入依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- MyBatis -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>
        <!-- mysql驱动 依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.25</version>
            <scope>runtime</scope>
        </dependency>
        <!--Druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--shiro-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.1</version>
        </dependency>
        <!--shiro标签 xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro" -->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
    </dependencies>

2、书写配置类

@Configuration
public class ShiroConfig {
    @Bean
    public ShiroDialect getShiroDialect() {
        return new ShiroDialect();
    }

    @Bean//realm
    public MyRealm getMyRealm(DataSource dataSource) {
        MyRealm myRealm = new MyRealm();
        return myRealm;
    }

    @Bean//安全管理器
    public DefaultWebSecurityManager getDefaultWebSecurityManager(MyRealm myRealm) {
        DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
        defaultSecurityManager.setRealm(myRealm);//SecurityManager完成校验需要realm
        return defaultSecurityManager;
    }

    @Bean//过滤器
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultSecurityManager securityManager) {
        ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
        //过滤器是shiro执行权限的核心,进行认证和授权是需要SecurityManager的
        filter.setSecurityManager(securityManager);
        //设置shiro的拦截规则
        Map<String, String> filterMap = new HashMap<>();
        //user:使用remberme的用户可访问
        //perms:对应权限可访问
        //role:对应的角色才能访问
        filterMap.put("/", "anon");//anon表示不拦截(匿名用户可访问)
        filterMap.put("/login.html", "anon");
        filterMap.put("/regist.html", "anon");
        filterMap.put("/user/login", "anon");
        filterMap.put("/user/regist", "anon");
        filterMap.put("/static/**", "anon");
        filterMap.put("/index.html", "anon");
        filterMap.put("/**", "authc");//authc表示认证用户可访问
        filter.setFilterChainDefinitionMap(filterMap);
        filter.setLoginUrl("/login.html");
        //设置未授权访问的页面
        filter.setUnauthorizedUrl("/login.html");
        return filter;
    }
}

       配置类中的Realm需要自定义,在使用ini文件作为数据源的时候使用的是lniRealm,使用数据库作为数据源的时候使用的是JdbcRealm,JdbcRealm中的数据库的表名称与表的字段名称都是固定的。在使用自定义Realm之后数据库不是固定不变的,但是dao层需要我们自己书写代码来完成实现,配置类中的Realm也需要我们自己定义。

3、自定义Realm

public class MyRealm extends AuthorizingRealm {//实现了接口的类才是一个realm类
    @Autowired
    private UserDao userDao;
    @Autowired
    private RoleDao roleDao;
    @Autowired
    private PermissionDao permissionDao;

    public String getName() {
        return "myRealm";
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //获取当前认证的用户的用户名,跟doGetAuthenticationInfo方法中的new SimpleAuthenticationInfo参数是对应的,获取到的数据与认证的时候封装的数据是对应的
        String username= (String) principalCollection.iterator().next();
        //根据用户名查询当前用户的前台列表,Set集合能够去除重复的数据
        Set<String> roleNames=roleDao.queryRoleNameByUsername(username);
        Set<String> ps=permissionDao.queryPermissionsByUsername(username);
        SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
        info.setRoles(roleNames);
        info.setStringPermissions(ps);
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //参数authenticationToken就是传递的subject.login(token)中的参数
        UsernamePasswordToken token= (UsernamePasswordToken) authenticationToken;
        //从token中获取用户名
        String username=token.getUsername();
//根据用户名查询用户的安全数据(数据库中的用户数据) User user
= userDao.getUserByUsername(username); if(user==null){ return null; } AuthenticationInfo info=new SimpleAuthenticationInfo(//将密码封装为shiro需要的格式 username,//当前用户用户名,跟上面的doGetAuthorizationInfo方法是对应的 user.getUserPwd(),//从数据库查询出来的安全密码 getName()); return info; } }
  • 创建一个类并继承AuthorizingRealm类
  • 重写doGetAuthorizationInfo获取授权信息和doGetAuthenticationInfo方法来获取认证信息
  • 重写getName方法返回当前realm的一个自定义名称

4、controller层

(1)页面跳转所需要的controller

@Controller
public class PageController {
    @RequestMapping("/login.html")
    public String login(){
        return "login";
    }

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

    @RequestMapping("/index.html")
    public String index(){
        return "index";
    }
}

(2)用户登录的controller

@Controller
@RequestMapping("user")
public class UserController {
    @Autowired
    private UserService userService;
    @RequestMapping("login")
    public String login(String username,String password){
        try{
            userService.checkLogin(username,password);
            System.out.println("成功");
            System.out.println(username+password);
            return "index";
        }catch (Exception e){
            e.printStackTrace();
            System.out.println("失败");
            System.out.println(username+password);
            return "login";
        }
    }
}

5、service层

@Service
public class UserService {
    public void checkLogin(String username,String password) throws Exception{
        Subject subject= SecurityUtils.getSubject();
        UsernamePasswordToken token=new UsernamePasswordToken(username,password);
        subject.login(token);
    }
}

完成用户的认证

6、dao层

用户:

@Repository
public interface UserDao {
     User getUserByUsername(String username);//用户名获取用户
}
<?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.zhb.dao.UserDao">
    <resultMap id="userMap" type="User">
        <id column="user_id" property="userId"></id>
        <result column="username" property="userName"></result>
        <result column="password" property="userPwd"></result>
        <result column="password_salt" property="pwdSalt"></result>
    </resultMap>
    <select id="getUserByUsername" resultMap="userMap">
        select * from tb_users
        where username=#{username}
    </select>
</mapper>

角色:

@Repository
public interface RoleDao {
    public Set<String> queryRoleNameByUsername(String username);//用户名获取角色
}
<?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.zhb.dao.RoleDao">
    <select id="queryRoleNameByUsername" resultType="String" resultSets="java.util.Set">
        SELECT role_name
        FROM tb_users INNER JOIN tb_urs ON tb_users.user_id = tb_urs.uid
        INNER JOIN tb_roles ON tb_urs.rid = tb_roles.role_id
        WHERE tb_users.username=#{username}
    </select>
</mapper>

权限:

@Repository
public interface PermissionDao {
    public Set<String> queryPermissionsByUsername(String username);
}
<?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.zhb.dao.PermissionDao">
    <select id="queryPermissionsByUsername" resultType="String" resultSets="java.util.Set">
        SELECT tb_permissions.permission_code FROM tb_users
        INNER JOIN tb_urs ON tb_users.user_id = tb_urs.uid
        INNER JOIN tb_roles ON tb_urs.rid = tb_roles.role_id
        INNER JOIN tb_rps ON tb_roles.role_id = tb_rps.rid
        INNER JOIN tb_permissions ON tb_rps.pid = tb_permissions.permission_id
        WHERE tb_users.username=#{username}
    </select>
</mapper>

7、bean

@Data
public class User {
    private Integer userId;
    private String userName;
    private String userPwd;
    private String pwdSalt;
}

8、yml配置文件

spring:
  datasource:
    druid:
      username: root
      password: root
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/myshiro?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
      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
# mybatis配置
mybatis:
  mapper-locations: classpath:mappers/*.xml    # mapper映射文件位置
  type-aliases-package: com.zhb.beans    # 实体类所在的位置
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl   #用于控制台打印sql语句

配置数据源与mybatis,其中mybatis配置xml文件的位置、实体类位置、以及控制台打印SQL语句

9、页面

登录页:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>
    <form action="/user/login">
        <input type="text" name="username">
        <input type="password" name="password">
        <input type="submit" value="提交">
    </form>
</body>
</html>

主页:

<!DOCTYPE html>
<html lang="en" xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
<h3>index</h3>
<hr/>
<shiro:guest>
    欢迎游客访问<a href="login.html">登录</a>
</shiro:guest>
<shiro:user>
    用户[
    <shiro:principal/>
    ]登录成功
    当前用户的身份
    <shiro:hasRole name="管理员">管理员</shiro:hasRole>
    <shiro:hasRole name="学生">学生</shiro:hasRole>
    <hr/>
    学生:
    <ul>
        <shiro:hasPermission name="stu_select">
            <li><a href="#">查询</a></li>
        </shiro:hasPermission>
    </ul>
    <hr/>
    教师:
    <ul>
        <shiro:hasPermission name="tea_select">
            <li><a href="#">查询</a></li>
        </shiro:hasPermission>
    </ul>
    <ul>
        <shiro:hasPermission name="tea_delete">
            <li><a href="#">删除</a></li>
        </shiro:hasPermission>
    </ul>
    <ul>
        <shiro:hasPermission name="tea_update">
            <li><a href="#">修改</a></li>
        </shiro:hasPermission>
    </ul>
    管理员:
    <ul>
        <shiro:hasPermission name="man_select">
            <li><a href="#">查询</a></li>
        </shiro:hasPermission>
    </ul>
    <ul>
        <shiro:hasPermission name="man_delete">
            <li><a href="#">删除</a></li>
        </shiro:hasPermission>
    </ul>
    <ul>
        <shiro:hasPermission name="man_update">
            <li><a href="#">修改</a></li>
        </shiro:hasPermission>
    </ul>
    <ul>
        <shiro:hasPermission name="man_add">
            <li><a href="#">添加</a></li>
        </shiro:hasPermission>
    </ul>
</shiro:user>
</body>
</html>

11、数据库设计(五张表)

(1)用户表

(2)用户角色表

(3)角色表

(4)角色权限表

(5)权限

   这些表都是自定义的,不是shiro默认的表,字段也不局限于shiro默认的表的字段,因为dao层是我们自己实现的。

11、测试

(1)管理员

 (2)学生

 (3)教师

每个人都会有一段异常艰难的时光 。 生活的压力 , 工作的失意 , 学业的压力。 爱的惶惶不可终日。 挺过来的 ,人生就会豁然开朗。 挺不过来的 ,时间也会教你 ,怎么与它们握手言和 ,所以不必害怕的。 ——杨绛
原文地址:https://www.cnblogs.com/zhai1997/p/13719489.html