springboot + shiro + mysql + mybatis 工程快速搭建
1. 新建 springboot 工程
2. 随便起个名字
3. 初始化工程
4. 导入 shiro 和 thymeleaf 依赖
<!-- thymeleaf依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!-- thymeleaf依赖结束 --> <!-- shiro依赖 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.3.2</version> </dependency> <!-- shiro依赖结束 -->
5. 编写 application.yml 配置文件
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url:
jdbc:mysql:///test?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true
username: root password: root thymeleaf: encoding: UTF-8
6. 新建一个 User 类
import lombok.Data; @Data public class User { private Integer id; private String username; private String password; private String prems; }
7. 创建 User 业务层与持久层
UserService
import com.example.exam01.entity.User; /** * User 业务层 */ public interface UserService { User findByName(String username); }
UserServiceImpl
import com.example.exam01.dao.UserDao; import com.example.exam01.entity.User; import com.example.exam01.service.UserService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; /** * User 业务层实现类 */ @Service @Transactional public class UserServiceImpl implements UserService { @Resource private UserDao userDao; @Override public User findByName(String username) { return userDao.findByName(username); } }
UserDao
import com.example.exam01.entity.User; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.springframework.stereotype.Repository; /** * User 持久层 */ @Repository @Mapper public interface UserDao { @Select("SELECT * FROM user WHERE username = #{username}") User findByName(@Param("username") String username); }
8. 新建一个 ShiroConfig 配置文件类
import com.example.exam01.shiro.realm.MyRealm; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; /** * shiro 配置类 */ @Configuration public class ShiroConfig { @Bean(name = "shiroFilter") public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); shiroFilterFactoryBean.setLoginUrl("/toLogin"); shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth"); // 定义一个map集合用来存放访问规则 Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); /* Shiro内置过滤器, 可以实现权限相关的拦截器 常用的过滤器: anon: 无需认证(登录)可以访问 authc: 必须认证才可以访问 user: 使用 rememberMe 的功能可以直接访问 perms: 该资源必须得到资源权限才可以访问 role: 该资源必须得到角色权限才可以访问 */ // 注意配置顺序 filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/", "anon"); filterChainDefinitionMap.put("/admin/**", "perms[user:admin]"); filterChainDefinitionMap.put("/user/**", "authc"); filterChainDefinitionMap.put("/logout", "authc"); //主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截 剩余的都需要认证 filterChainDefinitionMap.put("/**", "authc"); // 将规则写入 shiroFilterFactoryBean 中 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } /** * 获取 SecurityManager * @return */ @Bean public SecurityManager securityManager() { DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager(); defaultSecurityManager.setRealm(myRealm()); return defaultSecurityManager; } /** * 获取 MyRealm * @return */ @Bean public MyRealm myRealm() { MyRealm myRealm = new MyRealm(); return myRealm; } }
9. 新建一个 Realm 类
import com.example.exam01.entity.User; import com.example.exam01.service.UserService; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; 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.subject.Subject; import javax.annotation.Resource; import java.util.HashSet; import java.util.Set; /** * realm类 */ public class MyRealm extends AuthorizingRealm { @Resource private UserService userService; /** * 授权 * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { // 获取当前登录用户 Subject subject = SecurityUtils.getSubject(); User user = (User) subject.getPrincipal(); // 获取 SimpleAuthorizationInfo 对象写入授权规则 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // 创建一个 set 集合用来保存当前用户的授权信息 Set<String> stringSet = new HashSet<>(); stringSet.add(user.getPrems()); // 将授权信息写入 SimpleAuthorizationInfo 对象中 info.setStringPermissions(stringSet); return info; } /** * 认证 * @param auToken * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auToken) throws AuthenticationException { // AuthenticationToken 强转 UsernamePasswordToken UsernamePasswordToken token = (UsernamePasswordToken) auToken; // 从数据库获取用户信息 User user = userService.findByName(token.getUsername()); return new SimpleAuthenticationInfo(user, user.getPassword(),getName()); } }
10. 编写 controller 层
LoginController
package com.example.exam01.controller; import com.example.exam01.entity.User; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.crypto.hash.SimpleHash; import org.apache.shiro.subject.Subject; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; /** * Login 控制类 */ @Controller public class LoginController { // 跳转登录页面 @RequestMapping("/toLogin") public String toLogin(){ return "login"; } // 执行登录方法 @RequestMapping("/login") public String login(User user, Model model){ // 执行加密算法 SimpleHash md5 = new SimpleHash("MD5",user.getPassword(),null,1); String password = md5.toString(); // 获取 subject 对象 Subject subject = SecurityUtils.getSubject(); // 准备 token 令牌 UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),password); // 定义一个返回提示信息容器 String msg = null; // 执行认证登录 try{ subject.login(token); } catch (UnknownAccountException uae) { msg = "未知账户"; } catch (IncorrectCredentialsException ice) { msg = "密码不正确"; } catch (LockedAccountException lae) { msg = "账户已锁定"; } catch (ExcessiveAttemptsException eae) { msg = "用户名或密码错误次数过多"; } catch (AuthenticationException ae) { msg = "用户名或密码不正确"; } // 判断登录是否成功 if (subject.isAuthenticated()) { return "main"; } else { token.clear(); // 写入返回 tips model.addAttribute("msg",msg); return "login"; } } // 执行登出方法 @RequestMapping("/logout") public String logout(){ Subject subject = SecurityUtils.getSubject(); subject.logout(); return "login"; } // 跳转错误页面 @RequestMapping("/noAuth") public String noAuth(){ return "noAuth"; } }
UserController
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/user") public class UserController { @RequestMapping("list") public String list(){ return "/user/userList"; } }
AdminController
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/admin") public class AdminController { @RequestMapping("/list") public String list(){ return "/admin/adminList"; } }
11. 编写 HTML 页面
login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<h3 th:text="${msg}" style="color: red"></h3>
<form action="/login" method="post">
<label>账号: <input type="text" name="username" placeholder="请输入用户名"></label><br>
<label>密码: <input type="password" name="password" placeholder="请输入密码"></label><br>
<input type="submit" value="登录">
</form>
</body>
</html>
main.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>主页</title> </head> <body> <a href="/logout"><button>退出登录</button></a> <hr> <a href="/user/list">UserList</a> <br> <a href="/admin/list">AdminList</a> </body> </html>
noAuth.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>错误页面</title>
</head>
<body>
您没有此权限!
</body>
</html>
adminList.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>AdminList</title>
</head>
<body>
AdminList 只是一个需要 admin 权限才能访问的页面
</body>
</html>
userList.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>UserList</title>
</head>
<body>
UserList 这是一个需要登录才能访问的页面
</body>
</html>
12. 编写数据库
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键', `username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户名', `password` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '密码', `prems` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '权限', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; INSERT INTO `user` VALUES (1, 'lilei', '202cb962ac59075b964b07152d234b70', 'user:admin'); INSERT INTO `user` VALUES (2, 'hanmeimei', '202cb962ac59075b964b07152d234b70', 'user:user'); SET FOREIGN_KEY_CHECKS = 1;
源码下载: springboot + shiro demo 下载地址