🔥使用springBoot快速集成Shiro框架(附源码)
前言
??在编写SpringBoot过程中,需要配置shiro,但是却无从下手那么可以看看这篇文章,让你快速在SpringBoot项目里面集成shiro 希望可以给读到此文章的读者带去帮助,这样这篇文章也有了意义。 如果觉得有帮助可以**点赞收藏**支持一下,能关注一下就再好不过???
一、springBoot快速集成Shiro
配置依赖
导入shiro依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
导入mybatis-plus依赖
- 导入mybatis-plus依赖是为了验证用户的用户名和密码,同时可以调用权限表去给用户配置权限,也可以用mybatis,
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>2.1.9</version>
</dependency>
注意mybatis的配置和mybatis-plus的配置不同
#mybatis配置
mybatis: #设置mapper的路径和实体
mapper-locations: classpath*:mapper
mybatis-plus:
configuration: # 支持原生Mybatis的配置,就是既可以用方法调用也可以写sql
map-underscore-to-camel-case: true # 是否开启自动驼峰命名规则,默认false
auto-mapping-behavior: full # 自动映射策略,可以指定mybatis是否并且如何来自动映射数据表字段对象属性
#有3个可选值,full对所有的resultMap都进行自动映射
## 是否将sql打印到控制?板(该配置会将sql语句和查询的结果都打印到控制台)
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 指定mybatis的xml配置文件位置
mapper-locations: classpath*:mapper*Mapper.xml
数据库的设计
需要三个数据表 User: 用来存储用户的密码,用户名等等信息。 Role: 角色表,存放所有的角色信息 Auth:权限表,定义了一些操作访问权限信息。
例如
配置Realms
配置自己的AdminAuthorizingRealm
- 1 . AdminAuthorizingRealm继承AuthorizingRealm ,默认重写验证和权限方法,验证和授权的逻辑全部写在Realm中,只需要从我们的数据表中把用户和用户的角色权限信息取出来交给Shiro即可。Shiro就会自动的进行权限判断
public class AdminAuthorizingRealm extends AuthorizingRealm {
@Autowired
private LitemallAdminService adminService;
@Autowired
private LitemallRoleService roleService;
@Autowired
private LitemallPermissionService permissionService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
if (principals == null) {
throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
}
LitemallAdmin admin = (LitemallAdmin) getAvailablePrincipal(principals);
Integer[] roleIds = admin.getRoleIds();
Set<String> roles = roleService.queryByIds(roleIds);
Set<String> permissions = permissionService.queryByRoleIds(roleIds);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setRoles(roles);
info.setStringPermissions(permissions);
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
String username = upToken.getUsername();
String password = new String(upToken.getPassword());
if (StringUtils.isEmpty(username)) {
throw new AccountException("用户名不能为空");
}
if (StringUtils.isEmpty(password)) {
throw new AccountException("密码不能为空");
}
List<LitemallAdmin> adminList = adminService.findAdmin(username);
Assert.state(adminList.size() < 2, "同一个用户名存在两个账户");
if (adminList.size() == 0) {
throw new UnknownAccountException("找不到用户(" + username + ")的帐号信息");
}
LitemallAdmin admin = adminList.get(0);
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
if (!encoder.matches(password, admin.getPassword())) {
throw new UnknownAccountException("找不到用户(" + username + ")的帐号信息");
}
return new SimpleAuthenticationInfo(admin, password, getName());
}
}
配置的是ShiroConfig类
Filter通过URL规则去进行过滤和权限校验
package org.linlinjava.litemall.admin.config;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.linlinjava.litemall.admin.shiro.AdminAuthorizingRealm;
import org.linlinjava.litemall.admin.shiro.AdminWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean
public Realm realm() {
return new AdminAuthorizingRealm();
}
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
filterChainDefinitionMap.put("/admin/auth/kaptcha", "anon");
filterChainDefinitionMap.put("/admin/auth/login", "anon");
filterChainDefinitionMap.put("/admin/auth/401", "anon");
filterChainDefinitionMap.put("/admin/auth/index", "anon");
filterChainDefinitionMap.put("/admin/auth/403", "anon");
filterChainDefinitionMap.put("/admin/index/*", "anon");
filterChainDefinitionMap.put("/admin/**", "authc");
shiroFilterFactoryBean.setLoginUrl("/admin/auth/unlogin");
shiroFilterFactoryBean.setSuccessUrl("/admin/auth/index");
shiroFilterFactoryBean.setUnauthorizedUrl("/admin/auth/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean
public SessionManager sessionManager() {
return new AdminWebSessionManager();
}
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm());
securityManager.setSessionManager(sessionManager());
return securityManager;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =
new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public static DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
creator.setProxyTargetClass(true);
return creator;
}
}
自定义sessionManager
问题分析与解决方案
import com.alibaba.druid.util.StringUtils;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;
public class AdminWebSessionManager extends DefaultWebSessionManager {
public static final String LOGIN_TOKEN_KEY = "X-Litemall-Admin-Token";
private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";
public AdminWebSessionManager() {
super();
setGlobalSessionTimeout(MILLIS_PER_HOUR * 6);
setSessionIdUrlRewritingEnabled(false);
}
@Override
protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
String id = WebUtils.toHttp(request).getHeader(LOGIN_TOKEN_KEY);
if (!StringUtils.isEmpty(id)) {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
return id;
} else {
return super.getSessionId(request, response);
}
}
}
修改Controller中的登录请求
package org.linlinjava.litemall.admin.web;
import com.google.code.kaptcha.Producer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.subject.Subject;
import org.linlinjava.litemall.admin.service.LogHelper;
import org.linlinjava.litemall.admin.util.Permission;
import org.linlinjava.litemall.admin.util.PermissionUtil;
import org.linlinjava.litemall.core.util.IpUtil;
import org.linlinjava.litemall.core.util.JacksonUtil;
import org.linlinjava.litemall.core.util.ResponseUtil;
import org.linlinjava.litemall.db.domain.LitemallAdmin;
import org.linlinjava.litemall.db.service.LitemallAdminService;
import org.linlinjava.litemall.db.service.LitemallPermissionService;
import org.linlinjava.litemall.db.service.LitemallRoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import static org.linlinjava.litemall.admin.util.AdminResponseCode.*;
@RestController
@RequestMapping("/admin/auth")
@Validated
public class AdminAuthController {
private final Log logger = LogFactory.getLog(AdminAuthController.class);
@Autowired
private LitemallAdminService adminService;
@Autowired
private LitemallRoleService roleService;
@Autowired
private LitemallPermissionService permissionService;
@Autowired
private LogHelper logHelper;
@Autowired
private Producer kaptchaProducer;
@GetMapping("/kaptcha")
public Object kaptcha(HttpServletRequest request) {
String kaptcha = doKaptcha(request);
if (kaptcha != null) {
return ResponseUtil.ok(kaptcha);
}
return ResponseUtil.fail();
}
private String doKaptcha(HttpServletRequest request) {
String text = kaptchaProducer.createText();
BufferedImage image = kaptchaProducer.createImage(text);
HttpSession session = request.getSession();
session.setAttribute("kaptcha", text);
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageIO.write(image, "jpeg", outputStream);
String base64 = Base64.getEncoder().encodeToString(outputStream.toByteArray());
return "data:image/jpeg;base64," + base64.replaceAll("\r\n", "");
} catch (IOException e) {
return null;
}
}
@PostMapping("/login")
public Object login(@RequestBody String body, HttpServletRequest request) {
String username = JacksonUtil.parseString(body, "username");
String password = JacksonUtil.parseString(body, "password");
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
return ResponseUtil.badArgument();
}
Subject currentUser = SecurityUtils.getSubject();
try {
currentUser.login(new UsernamePasswordToken(username, password));
} catch (UnknownAccountException uae) {
logHelper.logAuthFail("登录", "用户帐号或密码不正确");
return ResponseUtil.fail(ADMIN_INVALID_ACCOUNT, "用户帐号或密码不正确", doKaptcha(request));
} catch (LockedAccountException lae) {
logHelper.logAuthFail("登录", "用户帐号已锁定不可用");
return ResponseUtil.fail(ADMIN_INVALID_ACCOUNT, "用户帐号已锁定不可用");
} catch (AuthenticationException ae) {
logHelper.logAuthFail("登录", "认证失败");
return ResponseUtil.fail(ADMIN_INVALID_ACCOUNT, "认证失败");
}
currentUser = SecurityUtils.getSubject();
LitemallAdmin admin = (LitemallAdmin) currentUser.getPrincipal();
admin.setLastLoginIp(IpUtil.getIpAddr(request));
admin.setLastLoginTime(LocalDateTime.now());
adminService.updateById(admin);
logHelper.logAuthSucceed("登录");
Map<String, Object> adminInfo = new HashMap<String, Object>();
adminInfo.put("nickName", admin.getUsername());
adminInfo.put("avatar", admin.getAvatar());
Map<Object, Object> result = new HashMap<Object, Object>();
result.put("token", currentUser.getSession().getId());
result.put("adminInfo", adminInfo);
return ResponseUtil.ok(result);
}
@RequiresAuthentication
@PostMapping("/logout")
public Object logout() {
Subject currentUser = SecurityUtils.getSubject();
logHelper.logAuthSucceed("退出");
currentUser.logout();
return ResponseUtil.ok();
}
@RequiresAuthentication
@GetMapping("/info")
public Object info() {
Subject currentUser = SecurityUtils.getSubject();
LitemallAdmin admin = (LitemallAdmin) currentUser.getPrincipal();
Map<String, Object> data = new HashMap<>();
data.put("name", admin.getUsername());
data.put("avatar", admin.getAvatar());
Integer[] roleIds = admin.getRoleIds();
Set<String> roles = roleService.queryByIds(roleIds);
Set<String> permissions = permissionService.queryByRoleIds(roleIds);
data.put("roles", roles);
data.put("perms", toApi(permissions));
return ResponseUtil.ok(data);
}
@Autowired
private ApplicationContext context;
private HashMap<String, String> systemPermissionsMap = null;
private Collection<String> toApi(Set<String> permissions) {
if (systemPermissionsMap == null) {
systemPermissionsMap = new HashMap<>();
final String basicPackage = "org.linlinjava.litemall.admin";
List<Permission> systemPermissions = PermissionUtil.listPermission(context, basicPackage);
for (Permission permission : systemPermissions) {
String perm = permission.getRequiresPermissions().value()[0];
String api = permission.getApi();
systemPermissionsMap.put(perm, api);
}
}
Collection<String> apis = new HashSet<>();
for (String perm : permissions) {
String api = systemPermissionsMap.get(perm);
apis.add(api);
if (perm.equals("*")) {
apis.clear();
apis.add("*");
return apis;
}
}
return apis;
}
@GetMapping("/401")
public Object page401() {
return ResponseUtil.unlogin();
}
@GetMapping("/index")
public Object pageIndex() {
return ResponseUtil.ok();
}
@GetMapping("/403")
public Object page403() {
return ResponseUtil.unauthz();
}
}
设置角色权限
例如
package org.linlinjava.litemall.admin.web;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
import org.linlinjava.litemall.admin.service.LogHelper;
import org.linlinjava.litemall.core.util.RegexUtil;
import org.linlinjava.litemall.core.util.ResponseUtil;
import org.linlinjava.litemall.core.util.bcrypt.BCryptPasswordEncoder;
import org.linlinjava.litemall.core.validator.Order;
import org.linlinjava.litemall.core.validator.Sort;
import org.linlinjava.litemall.db.domain.LitemallAdmin;
import org.linlinjava.litemall.db.service.LitemallAdminService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.NotNull;
import java.util.List;
import static org.linlinjava.litemall.admin.util.AdminResponseCode.*;
@RestController
@RequestMapping("/admin/admin")
@Validated
public class AdminAdminController {
private final Log logger = LogFactory.getLog(AdminAdminController.class);
@Autowired
private LitemallAdminService adminService;
@Autowired
private LogHelper logHelper;
@RequiresPermissions("admin:admin:list")
@RequiresPermissionsDesc(menu = {"系统管理", "管理员管理"}, button = "查询")
@GetMapping("/list")
public Object list(String username,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer limit,
@Sort @RequestParam(defaultValue = "add_time") String sort,
@Order @RequestParam(defaultValue = "desc") String order) {
List<LitemallAdmin> adminList = adminService.querySelective(username, page, limit, sort, order);
return ResponseUtil.okList(adminList);
}
}
}
shiro的核心组件
shiro的运行机制
希望对各位的学习有帮助,也希望小伙伴们多多支持,留下你们的爱心💕和赞👍! 💗最后最佳听众祝大家能够拿到心仪的Offer💗
|