一、pom.xml引入依赖
1、shiro依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.9.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.9.0</version>
</dependency>
2、缓存依赖(看需求)
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.5.3</version>
</dependency>
3、shiro注解权限控制依赖 及 shiro标签依赖(看需求)
<!--shiro注解权限控制-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--shiro标签-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
二、yaml配置文件
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/rbac?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123
type: com.alibaba.druid.pool.DruidDataSource
mybatis:
typeAliasesPackage: com.wnxy.springbootshiro.entity
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
lazy-loading-enabled: true
aggressive-lazy-loading: false
mapper-locations: classpath:mapper/*.xml
server:
servlet:
session:
tracking-modes: cookie
三、创建ehcache.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<ehcache >
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
四、应用程序入口类上添加注解@MapperScan,扫描mapper层
五、实现各层代码
mapper.xml文件
<select id="selectSysUserByTel" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from sys_user
where telephone = #{tel}
</select>
<select id="selectRolesByTel" resultType="java.lang.String">
select
sr.rolename
from sys_user left join sys_user_role sur on sys_user.id = sur.uid
left join sys_role sr on sr.id = sur.rid
where telephone = #{tel}
</select>
<select id="selectPersByTel" resultType="java.lang.String">
select
sp.NAME
from sys_user left join sys_user_role sur on sys_user.id = sur.uid
left join sys_role_permission srp on sur.rid = srp.rid
left join sys_permission sp on sp.id = srp.perid
where telephone = #{tel}
</select>
mapper接口
public interface SysUserMapper {
public SysUser selectSysUserByTel(String tel);
public Set<String> selectRolesByTel(String tel);
public Set<String> selectPersByTel(String tel);
service。。。
controller
@Controller
@RequestMapping("user")
@Slf4j
public class UserController {
@Autowired
private SysUserService sysUserService;
@GetMapping("loginDo")
public String loginDo(String tel, String password,@RequestParam(value = "isRememberMe",defaultValue = "0") Integer rememberMe){
try {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(tel, password);
//添加记住我功能
if (rememberMe==1) {
usernamePasswordToken.setRememberMe(true);
}
subject.login(usernamePasswordToken);
//得到session.并把user存放到session中
subject.getSession().setAttribute("user",subject.getPrincipal());
log.info("是否有管理员角色:{}",subject.hasRole("管理员"));
log.info("是否有用户管理权限:{}",subject.isPermitted("用户管理"));
return "redirect:../admin";
} catch (AuthenticationException e) {
return "redirect:../login";
}
}
@GetMapping("queryAll")
@RequiresRoles("管理员")
public String queryAll(@RequestParam(value = "userName",required = false)String userName, Model model){
List<SysUser> sysUsers = sysUserService.queryAll(userName);
model.addAttribute("list",sysUsers);
return "user/show";
}
@PostMapping("addDo")
public String addDo(SysUser sysUser){
sysUser.setPassword(MD5Util.getMD5(sysUser.getPassword(),sysUser.getTelephone()));
sysUserService.insertSelective(sysUser);
return "redirect:queryAll";
}
自定义realm
package com.wnxy.springbootshiro.config;
import com.wnxy.springbootshiro.entity.SysUser;
import com.wnxy.springbootshiro.service.SysUserService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Set;
/**
* @author Mrz
* @date 2022/8/16 11:21
*/
public class MyRealm extends AuthorizingRealm {
@Autowired
private SysUserService service;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SysUser sysUser = (SysUser) principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
Set<String> roles = service.selectRolesByTel(sysUser.getTelephone());
Set<String> pers = service.selectPersByTel(sysUser.getTelephone());
simpleAuthorizationInfo.setRoles(roles);
simpleAuthorizationInfo.setStringPermissions(pers);
return simpleAuthorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String tel = (String) authenticationToken.getPrincipal();
SysUser sysUser = service.selectSysUserByTel(tel);
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(sysUser, sysUser.getPassword(), getName());
simpleAuthenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(sysUser.getTelephone()));
return simpleAuthenticationInfo;
}
}
解决session丢失的拦截器
package com.wnxy.springbootshiro.util;
import com.wnxy.springbootshiro.entity.SysUser;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author Mrz
* @date 2022/8/16 19:08
*/
public class SessionInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Subject subject = SecurityUtils.getSubject();
if (!subject.isAuthenticated()&&subject.isRemembered()) {
Session session = subject.getSession();
if (session.getAttribute("user")==null) {
SysUser user = (SysUser) subject.getPrincipal();
session.setAttribute("user",user);
}
}
return true;
}
}
自定义MyWebMvcConfigurer:
package com.wnxy.springbootshiro.config;
import com.wnxy.springbootshiro.util.SessionInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author Mrz
* @date 2022/8/16 19:25
*/
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {
//添加拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SessionInterceptor()).addPathPatterns("/**").excludePathPatterns("/bootstrap/**","/login","/user/loginDo");
}
//添加视图控制
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("login").setViewName("login");
registry.addViewController("admin").setViewName("admin");
registry.addViewController("home").setViewName("home");
registry.addViewController("add").setViewName("user/add");
}
}
自定义ShiroConfig:
package com.wnxy.springbootshiro.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
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.HashMap;
import java.util.Map;
/**
* @author Mrz
* @date 2022/8/16 11:32
*/
@Configuration
public class ShiroConfig {
//配置缓存管理
@Bean
public EhCacheManager getEhCacheManager(){
EhCacheManager ehCacheManager = new EhCacheManager();
ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
return ehCacheManager;
}
//配置认证匹配器
@Bean
public HashedCredentialsMatcher getHashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("MD5");
hashedCredentialsMatcher.setHashIterations(1024);
return hashedCredentialsMatcher;
}
@Bean
public MyRealm getMyRealm(HashedCredentialsMatcher hashedCredentialsMatcher){
MyRealm myRealm = new MyRealm();
//将认证匹配器绑定到自定义的realm中
myRealm.setCredentialsMatcher(hashedCredentialsMatcher);
return myRealm;
}
@Bean
public DefaultSecurityManager getSecurityManager(MyRealm myRealm,EhCacheManager ehCacheManager){
//注意:生成安全管理者,我们使用的是针对web服务的DefaultWebSecurityManager
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(myRealm);
//绑定缓存管理器
defaultWebSecurityManager.setCacheManager(ehCacheManager);
return defaultWebSecurityManager;
}
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultSecurityManager defaultSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(defaultSecurityManager);
//拦截后跳转的路径
shiroFilterFactoryBean.setLoginUrl("login");
//身份认证失败,则跳转到登录页面的配置 没有登录的用户请求需要登录的页面时自动跳转到登录页面,不是必须的属性,不输入地址的话会自动寻找项目web项目的根目录下的”/login.jsp”页面。
// shiroFilter.setSuccessUrl("");//登录成功默认跳转页面,不配置则跳转至”/”。如果登陆前点击的一个需要登录的页面,则在登录自动跳转到那个需要登录的页面。不跳转到此。
// shiroFilter.setUnauthorizedUrl("");//没有权限默认跳转的页面
// shiroFilter.setFilterChainDefinitions("");//filterChainDefinitions的配置顺序为自上而下,以最上面的为准
//Shiro验证URL时,URL匹配成功便不再继续匹配查找(所以要注意配置文件中的URL顺序,尤其在使用通配符时)
// 配置不会被拦截的链接 顺序判断
Map<String,String> map = new HashMap<>();
//放行
map.put("/login","anon");
map.put("/user/loginDo","anon");
map.put("/bootstrap/**","anon");
//退出登录
map.put("/logout","logout");
//记住我
map.put("/**","user");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
//shiro注解配置通知
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultSecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
//配置方言,实现shiro标签
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
}
实现shiro标签html页面要加入标记属性
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
加密工具:
package com.wnxy.springbootshiro.util;
import org.apache.shiro.crypto.hash.SimpleHash;
/**
* @author Mrz
* @date 2022/8/16 12:04
*/
public class MD5Util {
public static String getMD5(String source,String salt){
SimpleHash md5 = new SimpleHash("MD5", source, salt, 1024);
return md5.toHex();
}
}
异常跳转页面:
package com.wnxy.springbootshiro.util;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.naming.AuthenticationException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author Mrz
* @date 2022/8/16 19:38
*/
@Component
public class MyException implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
if (ex instanceof UnauthorizedException || ex instanceof AuthenticationException) {
return new ModelAndView("errors/403");
}
return null;
}
}