1. 新建 springboot 工程
2. 随便起个名字
3. 初始化工程
4. 导入 shiro 和 thymeleaf 依赖
org.springframework.boot
spring-boot-starter-thymeleaf
org.apache.shiro
shiro-spring
1.3.2
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 类
importlombok.Data;
@Datapublic classUser {privateInteger id;privateString username;privateString password;privateString prems;
}
7. 创建 User 业务层与持久层
UserService
importcom.example.exam01.entity.User;/*** User 业务层*/
public interfaceUserService {
User findByName(String username);
}
UserServiceImpl
importcom.example.exam01.dao.UserDao;importcom.example.exam01.entity.User;importcom.example.exam01.service.UserService;importorg.springframework.stereotype.Service;importorg.springframework.transaction.annotation.Transactional;importjavax.annotation.Resource;/*** User 业务层实现类*/@Service
@Transactionalpublic class UserServiceImpl implementsUserService {
@ResourceprivateUserDao userDao;
@OverridepublicUser findByName(String username) {returnuserDao.findByName(username);
}
}
UserDao
importcom.example.exam01.entity.User;importorg.apache.ibatis.annotations.Mapper;importorg.apache.ibatis.annotations.Param;importorg.apache.ibatis.annotations.Select;importorg.springframework.stereotype.Repository;/*** User 持久层*/@Repository
@Mapperpublic interfaceUserDao {
@Select("SELECT * FROM user WHERE username = #{username}")
User findByName(@Param("username") String username);
}
8. 新建一个 ShiroConfig 配置文件类
importcom.example.exam01.shiro.realm.MyRealm;importorg.apache.shiro.mgt.SecurityManager;importorg.apache.shiro.spring.web.ShiroFilterFactoryBean;importorg.apache.shiro.web.mgt.DefaultWebSecurityManager;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importjava.util.LinkedHashMap;importjava.util.Map;/*** shiro 配置类*/@Configurationpublic classShiroConfig {
@Bean(name= "shiroFilter")publicShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean= newShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("/toLogin");
shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");//定义一个map集合用来存放访问规则
Map 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);returnshiroFilterFactoryBean;
}/*** 获取 SecurityManager
*@return
*/@BeanpublicSecurityManager securityManager() {
DefaultWebSecurityManager defaultSecurityManager= newDefaultWebSecurityManager();
defaultSecurityManager.setRealm(myRealm());returndefaultSecurityManager;
}/*** 获取 MyRealm
*@return
*/@BeanpublicMyRealm myRealm() {
MyRealm myRealm= newMyRealm();returnmyRealm;
}
}
9. 新建一个 Realm 类
importcom.example.exam01.entity.User;importcom.example.exam01.service.UserService;importorg.apache.shiro.SecurityUtils;import org.apache.shiro.authc.*;importorg.apache.shiro.authz.AuthorizationInfo;importorg.apache.shiro.authz.SimpleAuthorizationInfo;importorg.apache.shiro.realm.AuthorizingRealm;importorg.apache.shiro.subject.PrincipalCollection;importorg.apache.shiro.subject.Subject;importjavax.annotation.Resource;importjava.util.HashSet;importjava.util.Set;/*** realm类*/
public class MyRealm extendsAuthorizingRealm {
@ResourceprivateUserService userService;/*** 授权
*@paramprincipalCollection
*@return
*/@OverrideprotectedAuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {//获取当前登录用户
Subject subject =SecurityUtils.getSubject();
User user=(User) subject.getPrincipal();//获取 SimpleAuthorizationInfo 对象写入授权规则
SimpleAuthorizationInfo info = newSimpleAuthorizationInfo();//创建一个 set 集合用来保存当前用户的授权信息
Set stringSet = new HashSet<>();
stringSet.add(user.getPrems());//将授权信息写入 SimpleAuthorizationInfo 对象中
info.setStringPermissions(stringSet);returninfo;
}/*** 认证
*@paramauToken
*@return*@throwsAuthenticationException*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auToken) throwsAuthenticationException {//AuthenticationToken 强转 UsernamePasswordToken
UsernamePasswordToken token =(UsernamePasswordToken) auToken;//从数据库获取用户信息
User user =userService.findByName(token.getUsername());return newSimpleAuthenticationInfo(user, user.getPassword(),getName());
}
}
10. 编写 controller 层
LoginController
packagecom.example.exam01.controller;importcom.example.exam01.entity.User;importorg.apache.shiro.SecurityUtils;import org.apache.shiro.authc.*;importorg.apache.shiro.crypto.hash.SimpleHash;importorg.apache.shiro.subject.Subject;importorg.springframework.stereotype.Controller;importorg.springframework.ui.Model;importorg.springframework.web.bind.annotation.RequestMapping;/*** Login 控制类*/@Controllerpublic classLoginController {//跳转登录页面
@RequestMapping("/toLogin")publicString toLogin(){return "login";
}//执行登录方法
@RequestMapping("/login")publicString 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 = newUsernamePasswordToken(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")publicString logout(){
Subject subject=SecurityUtils.getSubject();
subject.logout();return "login";
}//跳转错误页面
@RequestMapping("/noAuth")publicString noAuth(){return "noAuth";
}
}
UserController
importorg.springframework.stereotype.Controller;importorg.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/user")public classUserController {
@RequestMapping("list")publicString list(){return "/user/userList";
}
}
AdminController
importorg.springframework.stereotype.Controller;importorg.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/admin")public classAdminController {
@RequestMapping("/list")publicString list(){return "/admin/adminList";
}
}
11. 编写 HTML 页面
login.html
登录账号:
密码:
main.html
主页noAuth.html
错误页面adminList.html
AdminListuserList.html
UserList12. 编写数据库
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;