下面介绍一下我在项目中如何将Apache Shiro整合入Spring Boot项目中的。Apache Shiro是一款功能强大,灵活的开源框架,不像Spring Security那么庞大和复杂,Shiro易于理解和使用。
先介绍下Apache Shiro的特性和架构,Shiro官方网站:http://shiro.apache.org/
Apache Shiro 特性:
Apache Shiro是一个综合的、有多种特性的应用安全框架。
- Authentication(认证):用户身份识别,通常被称为用户“登录”
- Authorization(授权):访问控制。比如某个用户是否具有某个操作的使用权限。
- Session Management(会话管理):特定于用户的会话管理,甚至在非web 或 EJB 应用程序。
- Cryptography(加密):在对数据源使用加密算法加密的同时,保证易于使用。
- Web Support(Web支持): Shiro’s web support APIs help easily secure web applications.
- Caching(缓存): Caching is a first-tier citizen in Apache Shiro’s API to ensure that security operations remain fast and efficient.
- Concurrency(并发): Apache Shiro supports multi-threaded applications with its concurrency features.
- Testing: Test support exists to help you write unit and integration tests and ensure your code will be secured as expected.
- “Run As”: A feature that allows users to assume the identity of another user (if they are allowed), sometimes useful in administrative scenarios.
- “Remember Me”: Remember users’ identities across sessions so they only need to log in when mandatory.
Apache Shiro有三大核心概念:Subject
, SecurityManager
and Realms
Subject:当前用户
SecurityManager: 管理所有的Subjects
Realm: Realm作为Shiro和访问你的应用安全数据的桥梁或者是连接器,这一部分是真正需要我们自己实现的
下面开始介绍将Shiro整合到Spring Boot中。
1. 数据库表准备
这里需要五张表,用户表,角色表,权限表,用户-角色表,角色-权限表,下面是建表语句
CREATE TABLE `sys_user` (
`user_id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL COMMENT '用户名',
`name` varchar(100) DEFAULT NULL,
`password` varchar(50) DEFAULT NULL COMMENT '密码',
`dept_id` int(20) DEFAULT NULL,
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`mobile` varchar(100) DEFAULT NULL COMMENT '手机号',
`status` tinyint(255) DEFAULT NULL COMMENT '状态 0:禁用,1:正常',
`user_id_create` bigint(255) DEFAULT NULL COMMENT '创建用户id',
`gmt_create` datetime DEFAULT NULL COMMENT '创建时间',
`gmt_modified` datetime DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=137 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES ('1', 'admin', '超级管理员', '27bd386e70f280e24c2f4f2a549b82cf', '6', 'admin@example.com', '123456', '1', '1', '2017-08-15 21:40:39', '2017-08-15 21:41:00');
INSERT INTO `sys_user` VALUES ('2', 'test', '临时用户', '6cf3bb3deba2aadbd41ec9a22511084e', '6', 'test@bootdo.com', null, '1', '1', '2017-08-14 13:43:05', '2017-08-14 21:15:36');
INSERT INTO `sys_user` VALUES ('36', 'ldh', '刘德华', 'bfd9394475754fbe45866eba97738c36', '6', 'ldh@bootdo.com', null, '1', null, null, null);
CREATE TABLE `sys_user_role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) DEFAULT NULL COMMENT '用户ID',
`role_id` bigint(20) DEFAULT NULL COMMENT '角色ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=127 DEFAULT CHARSET=utf8 COMMENT='用户与角色对应关系';
-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES ('73', '30', '48');
INSERT INTO `sys_user_role` VALUES ('74', '30', '49');
INSERT INTO `sys_user_role` VALUES ('75', '30', '50');
INSERT INTO `sys_user_role` VALUES ('76', '31', '48');
CREATE TABLE `sys_menu` (
`menu_id` bigint(20) NOT NULL AUTO_INCREMENT,
`parent_id` bigint(20) DEFAULT NULL COMMENT '父菜单ID,一级菜单为0',
`name` varchar(50) DEFAULT NULL COMMENT '菜单名称',
`url` varchar(200) DEFAULT NULL COMMENT '菜单URL',
`perms` varchar(500) DEFAULT NULL COMMENT '授权(多个用逗号分隔,如:user:list,user:create)',
`type` int(11) DEFAULT NULL COMMENT '类型 0:目录 1:菜单 2:按钮',
`icon` varchar(50) DEFAULT NULL COMMENT '菜单图标',
`order_num` int(11) DEFAULT NULL COMMENT '排序',
`gmt_create` datetime DEFAULT NULL COMMENT '创建时间',
`gmt_modified` datetime DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY (`menu_id`)
) ENGINE=InnoDB AUTO_INCREMENT=102 DEFAULT CHARSET=utf8 COMMENT='菜单管理';
-- ----------------------------
-- Records of sys_menu
-- ----------------------------
INSERT INTO `sys_menu` VALUES ('1', '0', '基础管理', '', '', '0', 'fa fa-bars', '0', '2017-08-09 22:49:47', null);
INSERT INTO `sys_menu` VALUES ('2', '3', '系统菜单', 'sys/menu/', 'sys:menu:menu', '1', 'fa fa-th-list', '2', '2017-08-09 22:55:15', null);
INSERT INTO `sys_menu` VALUES ('3', '0', '系统管理', null, null, '0', 'fa fa-desktop', '1', '2017-08-09 23:06:55', '2017-08-14 14:13:43');
INSERT INTO `sys_menu` VALUES ('6', '3', '用户管理', 'sys/user/', 'sys:user:user', '1', 'fa fa-user', '0', '2017-08-10 14:12:11', null);
CREATE TABLE `sys_user_role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) DEFAULT NULL COMMENT '用户ID',
`role_id` bigint(20) DEFAULT NULL COMMENT '角色ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=127 DEFAULT CHARSET=utf8 COMMENT='用户与角色对应关系';
-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES ('73', '30', '48');
INSERT INTO `sys_user_role` VALUES ('74', '30', '49');
CREATE TABLE `sys_role_menu` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`role_id` bigint(20) DEFAULT NULL COMMENT '角色ID',
`menu_id` bigint(20) DEFAULT NULL COMMENT '菜单ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2974 DEFAULT CHARSET=utf8 COMMENT='角色与菜单对应关系';
-- ----------------------------
-- Records of sys_role_menu
-- ----------------------------
INSERT INTO `sys_role_menu` VALUES ('367', '44', '1');
INSERT INTO `sys_role_menu` VALUES ('368', '44', '32');
2. 对应实体类
UserDO.java
package com.bootdo.system.domain;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
public class UserDO implements Serializable {
private static final long serialVersionUID = 1L;
//
private Long userId;
// 用户名
private String username;
// 用户真实姓名
private String name;
// 密码
private String password;
// 部门
private Long deptId;
private String deptName;
// 邮箱
private String email;
// 手机号
private String mobile;
// 状态 0:禁用,1:正常
private Integer status;
// 创建用户id
private Long userIdCreate;
// 创建时间
private Date gmtCreate;
// 修改时间
private Date gmtModified;
//角色
private List<Long> roleIds;
/**
* 设置:
*/
public void setUserId(Long userId) {
this.userId = userId;
}
/**
* 获取:
*/
public Long getUserId() {
return userId;
}
/**
* 设置:用户名
*/
public void setUsername(String username) {
this.username = username;
}
/**
* 获取:用户名
*/
public String getUsername() {
return username;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 设置:密码
*/
public void setPassword(String password) {
this.password = password;
}
/**
* 获取:密码
*/
public String getPassword() {
return password;
}
public Long getDeptId() {
return deptId;
}
public void setDeptId(Long deptId) {
this.deptId = deptId;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
/**
* 设置:邮箱
*/
public void setEmail(String email) {
this.email = email;
}
/**
* 获取:邮箱
*/
public String getEmail() {
return email;
}
/**
* 设置:手机号
*/
public void setMobile(String mobile) {
this.mobile = mobile;
}
/**
* 获取:手机号
*/
public String getMobile() {
return mobile;
}
/**
* 设置:状态 0:禁用,1:正常
*/
public void setStatus(Integer status) {
this.status = status;
}
/**
* 获取:状态 0:禁用,1:正常
*/
public Integer getStatus() {
return status;
}
/**
* 设置:创建用户id
*/
public void setUserIdCreate(Long userIdCreate) {
this.userIdCreate = userIdCreate;
}
/**
* 获取:创建用户id
*/
public Long getUserIdCreate() {
return userIdCreate;
}
/**
* 设置:创建时间
*/
public void setGmtCreate(Date gmtCreate) {
this.gmtCreate = gmtCreate;
}
/**
* 获取:创建时间
*/
public Date getGmtCreate() {
return gmtCreate;
}
/**
* 设置:修改时间
*/
public void setGmtModified(Date gmtModified) {
this.gmtModified = gmtModified;
}
/**
* 获取:修改时间
*/
public Date getGmtModified() {
return gmtModified;
}
public List<Long> getroleIds() {
return roleIds;
}
public void setroleIds(List<Long> roleIds) {
this.roleIds = roleIds;
}
@Override
public String toString() {
return "UserDO{" +
"userId=" + userId +
", username='" + username + '\'' +
", name='" + name + '\'' +
", password='" + password + '\'' +
", deptId=" + deptId +
", deptName='" + deptName + '\'' +
", email='" + email + '\'' +
", mobile='" + mobile + '\'' +
", status=" + status +
", userIdCreate=" + userIdCreate +
", gmtCreate=" + gmtCreate +
", gmtModified=" + gmtModified +
", roleIds=" + roleIds +
'}';
}
}
RoleDO.java
package com.bootdo.system.domain;
import java.sql.Timestamp;
import java.util.List;
public class RoleDO {
private Long roleId;
private String roleName;
private String roleSign;
private String remark;
private Long userIdCreate;
private Timestamp gmtCreate;
private Timestamp gmtModified;
private List<Long> menuIds;
public Long getRoleId() {
return roleId;
}
public void setRoleId(Long roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleSign() {
return roleSign;
}
public void setRoleSign(String roleSign) {
this.roleSign = roleSign;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public Long getUserIdCreate() {
return userIdCreate;
}
public void setUserIdCreate(Long userIdCreate) {
this.userIdCreate = userIdCreate;
}
public Timestamp getGmtCreate() {
return gmtCreate;
}
public void setGmtCreate(Timestamp gmtCreate) {
this.gmtCreate = gmtCreate;
}
public Timestamp getGmtModified() {
return gmtModified;
}
public void setGmtModified(Timestamp gmtModified) {
this.gmtModified = gmtModified;
}
public List<Long> getMenuIds() {
return menuIds;
}
public void setMenuIds(List<Long> menuIds) {
this.menuIds = menuIds;
}
@Override
public String toString() {
return "RoleDO{" +
"roleId=" + roleId +
", roleName='" + roleName + '\'' +
", roleSign='" + roleSign + '\'' +
", remark='" + remark + '\'' +
", userIdCreate=" + userIdCreate +
", gmtCreate=" + gmtCreate +
", gmtModified=" + gmtModified +
", menuIds=" + menuIds +
'}';
}
}
MenuDO.java
package com.bootdo.system.domain;
import java.io.Serializable;
import java.util.Date;
public class MenuDO implements Serializable {
private static final long serialVersionUID = 1L;
//
private Long menuId;
// 父菜单ID,一级菜单为0
private Long parentId;
// 菜单名称
private String name;
// 菜单URL
private String url;
// 授权(多个用逗号分隔,如:user:list,user:create)
private String perms;
// 类型 0:目录 1:菜单 2:按钮
private Integer type;
// 菜单图标
private String icon;
// 排序
private Integer orderNum;
// 创建时间
private Date gmtCreate;
// 修改时间
private Date gmtModified;
/**
* 设置:
*/
public void setMenuId(Long menuId) {
this.menuId = menuId;
}
/**
* 获取:
*/
public Long getMenuId() {
return menuId;
}
/**
* 设置:父菜单ID,一级菜单为0
*/
public void setParentId(Long parentId) {
this.parentId = parentId;
}
/**
* 获取:父菜单ID,一级菜单为0
*/
public Long getParentId() {
return parentId;
}
/**
* 设置:菜单名称
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取:菜单名称
*/
public String getName() {
return name;
}
/**
* 设置:菜单URL
*/
public void setUrl(String url) {
this.url = url;
}
/**
* 获取:菜单URL
*/
public String getUrl() {
return url;
}
/**
* 设置:授权(多个用逗号分隔,如:user:list,user:create)
*/
public void setPerms(String perms) {
this.perms = perms;
}
/**
* 获取:授权(多个用逗号分隔,如:user:list,user:create)
*/
public String getPerms() {
return perms;
}
/**
* 设置:类型 0:目录 1:菜单 2:按钮
*/
public void setType(Integer type) {
this.type = type;
}
/**
* 获取:类型 0:目录 1:菜单 2:按钮
*/
public Integer getType() {
return type;
}
/**
* 设置:菜单图标
*/
public void setIcon(String icon) {
this.icon = icon;
}
/**
* 获取:菜单图标
*/
public String getIcon() {
return icon;
}
/**
* 设置:排序
*/
public void setOrderNum(Integer orderNum) {
this.orderNum = orderNum;
}
/**
* 获取:排序
*/
public Integer getOrderNum() {
return orderNum;
}
/**
* 设置:创建时间
*/
public void setGmtCreate(Date gmtCreate) {
this.gmtCreate = gmtCreate;
}
/**
* 获取:创建时间
*/
public Date getGmtCreate() {
return gmtCreate;
}
/**
* 设置:修改时间
*/
public void setGmtModified(Date gmtModified) {
this.gmtModified = gmtModified;
}
/**
* 获取:修改时间
*/
public Date getGmtModified() {
return gmtModified;
}
@Override
public String toString() {
return "MenuDO{" +
"menuId=" + menuId +
", parentId=" + parentId +
", name='" + name + '\'' +
", url='" + url + '\'' +
", perms='" + perms + '\'' +
", type=" + type +
", icon='" + icon + '\'' +
", orderNum=" + orderNum +
", gmtCreate=" + gmtCreate +
", gmtModified=" + gmtModified +
'}';
}
}
UserRoleDO.java
package com.bootdo.system.domain;
public class UserRoleDO {
private Long id;
private Long userId;
private Long roleId;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public Long getRoleId() {
return roleId;
}
public void setRoleId(Long roleId) {
this.roleId = roleId;
}
@Override
public String toString() {
return "UserRoleDO{" +
"id=" + id +
", userId=" + userId +
", roleId=" + roleId +
'}';
}
}
RoleMenu.java
package com.bootdo.system.domain;
public class RoleMenuDO {
private Long id;
private Long roleId;
private Long menuId;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getRoleId() {
return roleId;
}
public void setRoleId(Long roleId) {
this.roleId = roleId;
}
public Long getMenuId() {
return menuId;
}
public void setMenuId(Long menuId) {
this.menuId = menuId;
}
@Override
public String toString() {
return "RoleMenuDO{" +
"id=" + id +
", roleId=" + roleId +
", menuId=" + menuId +
'}';
}
}
3. Shiro 配置,相当于XML里的配置
package com.bootdo.system.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.bootdo.system.shiro.UserRealm;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.MemorySessionDAO;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
@Configuration
public class ShiroConfig {
@Bean
public EhCacheManager getEhCacheManager() {
EhCacheManager em = new EhCacheManager();
em.setCacheManagerConfigFile("classpath:config/ehcache.xml");
return em;
}
@Bean
UserRealm userRealm(EhCacheManager cacheManager) {
UserRealm userRealm = new UserRealm();
userRealm.setCacheManager(cacheManager);
return userRealm;
}
@Bean
SessionDAO sessionDAO() {
MemorySessionDAO sessionDAO = new MemorySessionDAO();
return sessionDAO;
}
@Bean
public SessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
Collection<SessionListener> listeners = new ArrayList<SessionListener>();
listeners.add(new BDSessionListener());
sessionManager.setSessionListeners(listeners);
sessionManager.setSessionDAO(sessionDAO());
return sessionManager;
}
@Bean
SecurityManager securityManager(UserRealm userRealm) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(userRealm);
manager.setCacheManager(getEhCacheManager());
manager.setSessionManager(sessionManager());
return manager;
}
@Bean
ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("/login");
shiroFilterFactoryBean.setSuccessUrl("/index");
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
//配置访问权限控制规则
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/css/**", "anon"); //表示可以匿名访问
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/fonts/**", "anon");
filterChainDefinitionMap.put("/img/**", "anon");
filterChainDefinitionMap.put("/docs/**", "anon");
filterChainDefinitionMap.put("/druid/**", "anon");
filterChainDefinitionMap.put("/upload/**", "anon");
filterChainDefinitionMap.put("/files/**", "anon");
filterChainDefinitionMap.put("/logout", "logout");
filterChainDefinitionMap.put("/", "anon");
filterChainDefinitionMap.put("/blog", "anon");
filterChainDefinitionMap.put("/blog/open/**", "anon");
filterChainDefinitionMap.put("/**", "authc"); //表示需要认证才可以访问
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean("lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
proxyCreator.setProxyTargetClass(true);
return proxyCreator;
}
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
@Qualifier("securityManager") SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
4. Realm方法,实现认证和授权
package com.bootdo.system.shiro;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import com.bootdo.common.config.ApplicationContextRegister;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
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.springframework.beans.factory.annotation.Autowired;
import com.bootdo.common.utils.ShiroUtils;
import com.bootdo.system.dao.UserDao;
import com.bootdo.system.domain.UserDO;
import com.bootdo.system.service.MenuService;
public class UserRealm extends AuthorizingRealm {
/* @Autowired
UserDao userMapper;
@Autowired
MenuService menuService;*/
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
Long userId = ShiroUtils.getUserId();
MenuService menuService = ApplicationContextRegister.getBean(MenuService.class);
Set<String> perms = menuService.listPerms(userId);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setStringPermissions(perms);
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal();
Map<String, Object> map = new HashMap<>(16);
map.put("username", username);
String password = new String((char[]) token.getCredentials());
UserDao userMapper = ApplicationContextRegister.getBean(UserDao.class);
// 查询用户信息
UserDO user = userMapper.list(map).get(0);
// 账号不存在
if (user == null) {
throw new UnknownAccountException("账号或密码不正确");
}
// 密码错误
if (!password.equals(user.getPassword())) {
throw new IncorrectCredentialsException("账号或密码不正确");
}
// 账号锁定
if (user.getStatus() == 0) {
throw new LockedAccountException("账号已被锁定,请联系管理员");
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());
return info;
}
}
5. 接下来就是controller和页面了
LoginController.java
package com.bootdo.system.controller;
import com.bootdo.common.annotation.Log;
import com.bootdo.common.controller.BaseController;
import com.bootdo.common.domain.Tree;
import com.bootdo.common.utils.MD5Utils;
import com.bootdo.common.utils.R;
import com.bootdo.common.utils.ShiroUtils;
import com.bootdo.system.domain.MenuDO;
import com.bootdo.system.service.MenuService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
public class LoginController extends BaseController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
MenuService menuService;
@GetMapping({ "/", "" })
String welcome(Model model) {
return "redirect:/blog";
}
@Log("请求访问主页")
@GetMapping({ "/index" })
String index(Model model) {
List<Tree<MenuDO>> menus = menuService.listMenuTree(getUserId());
model.addAttribute("menus", menus);
model.addAttribute("name", getUser().getName());
model.addAttribute("username", getUser().getUsername());
return "index_v1";
}
@GetMapping("/login")
String login() {
return "login";
}
@Log("登录")
@PostMapping("/login")
@ResponseBody
R ajaxLogin(String username, String password) {
password = MD5Utils.encrypt(username, password);
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
return R.ok();
} catch (AuthenticationException e) {
return R.error("用户或密码错误");
}
}
@GetMapping("/logout")
String logout() {
ShiroUtils.logout();
return "redirect:/login";
}
@GetMapping("/main")
String main() {
return "main";
}
@GetMapping("/403")
String error403() {
return "403";
}
}
login.html
<div>
<form id="signupForm">
<h3 class="text-center">用户登录</h3>
<input type="text" name="username" class="form-control uname"
value="admin"/>
<input type="password" name="password"
class="form-control pword m-b" value="111111"/>
<button class="btn btn-login btn-block">登录</button>
</form>
</div>
以上是Spring Boot 整合Shiro的几大要素了,接下来就可以测试了,在没有登录的情况下,访问主页的时候会跳到登录的页面,而登录不同的用户也会随着用户所拥有的角色不同而显示不同的模块。