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)教師


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM