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