pom:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
</dependency>
Config:
-- MyRealm
package com.wang.config;
import com.wang.entity.TestUser;
import com.wang.entity.UserResouce;
import com.wang.service.TestUserServiceImpl;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
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.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import java.util.*;
/**
* @Author wangli
* @Date: 2022/3/10 10:39
* @Version 1.0
*/
@Configuration
public class MyRealm extends AuthorizingRealm {
@Autowired
private TestUserServiceImpl userService;
static Logger logger = LogManager.getLogger(MyRealm.class.getName());
// 授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
logger.info("进入了授权");
// Set<String> myRealm = principalCollection.getRealmNames("myRealm");
//获得当前用户
TestUser user = (TestUser) principalCollection.asList().get(0);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//需要绑定角色、资源
Collection<String> role = Collections.singleton(user.getRole());
info.addRoles(role);
// //拿到用户的权限-设置权限
List<String> permissions = userService.getUserPermission(user.getId());
info.addStringPermissions(permissions);
return info;
}
// 认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
logger.info("进入了认证");
//获得当前用户
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
System.out.println("userToken=>"+userToken);
String username = userToken.getUsername();
//获得数据库中的用户,来跟当前用户进行比对
TestUser user = userService.queryUserByUserName(username);
if (null == user) {//没有查到
return null;
}
ByteSource salt = ByteSource.Util.bytes("salt");//设置加盐
//完成了认证
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),salt,"myRealm");
return info;
}
}
-- ShiroConfig
package com.wang.config;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* @Author wangli
* @Date: 2022/3/10 10:35
* @Version 1.0
*/
@Configuration
public class ShiroConfig {
// 1.Realm 代表系统资源
// @Bean(name = "getRealm")
// public Realm getRealm(){
// return new MyRealm();
// }
// 2.SecurityManager 流程控制
@Bean(name = "SM")
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("myRealm")AuthorizingRealm MyRealm){//@Qualifier("getRealm")
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//设置加密方式(将输入的密码进行加密)
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("MD5");//设置加密算法
matcher.setHashIterations(3);//设置跌代次数
MyRealm.setCredentialsMatcher(matcher);
securityManager.setRealm(MyRealm);
return securityManager;
}
// 3.ShiroFilterFactoryBean 请求控制器
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("SM")DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(securityManager);
//配置路径过滤器
Map<String, String> filterMap = new HashMap<>();
//key是ant路径,value是shiro的默认过滤器
//shiro过滤器是DefaultFilter
/*
* anon,authc,perms,role
* 下面配置的认证或不认证是有先后顺序的,详细的配在前面才能生效
* 出前往后进行匹配
* perms[***]配置权限(有权限才能访问当前资源下的接口)
* */
filterMap.put("/comon/**","authc");
filterMap.put("/api/**","perms[vip1]");
filterMap.put("/salary/**","perms[vip2]");
// filterMap.put("/salary/**","authc,perms[salary],roles[admin]");
//设置登出
filterMap.put("/logout","logout");
factoryBean.setFilterChainDefinitionMap(filterMap);
//设置登录页面
// factoryBean.setLoginUrl("http://localhost:8080/");
//设置未授权的页面
// factoryBean.setUnauthorizedUrl();
return factoryBean;
}
}
-- MyExceptionHandler
package com.wang.config;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.naming.AuthenticationException;
/**
* @Author wangli
* @Date: 2022/3/11 17:08
* @Version 1.0
*/
@RestControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(AuthorizationException.class)
public Object shiroHandler(){
return "请先获取对应的资源,再进行访问!";
}
}
Controller (登录api)
package com.wang.controller;
import com.wang.entity.TestUser;
import com.wang.entity.User;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
/**
* @Author wangli
* @Date: 2022/3/10 10:26
* @Version 1.0
*/
@RequestMapping("/login")
@RestController
public class LoginController {
private static Logger log = LogManager.getLogger(LoginController.class.getName());
@PostMapping("/login")
public Object login(TestUser user) {
log.info(user);
Map<String, String> errorMsg = new HashMap<String, String>();
Subject currentUser = SecurityUtils.getSubject();
// if (!currentUser.isAuthenticated()) {//没认证过
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
try {
//登录入口
currentUser.login(token);//去登录
currentUser.getSession().setAttribute("currentUser", currentUser.getPrincipal());
System.out.println("登录成功用户信息=>"+currentUser.getPrincipal());
return "login Success";
} catch (UnknownAccountException uae) {
log.info("There is no user with username of " + token.getPrincipal());
errorMsg.put("errorMsg", "用户不存在");
} catch (IncorrectCredentialsException ice) {
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
errorMsg.put("errorMsg", "密码不正确");
} catch (LockedAccountException lae) {
log.info("The account for username " + token.getPrincipal() + " is locked. " +
"Please contact your administrator to unlock it.");
errorMsg.put("errorMsg", "账户已锁定");
}
// ... catch more exceptions here (maybe custom ones specific to your application?
catch (AuthenticationException ae) {
//unexpected condition? error?
log.info("登录失败");
errorMsg.put("errorMsg", "登录失败");
}
return errorMsg;
// }else {
//
// }
}
@RequiresAuthentication
@RequestMapping("/getCurrentUser")
public Object getCurrentUser(){
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession();
return session.getAttribute("currentUser");
}
//
@RequestMapping("/logout")
public void logout(){
Subject subject = SecurityUtils.getSubject();
subject.logout();
}
}
utils
--MD5PasswordUtil
package com.wang.utils;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.springframework.stereotype.Component;
import static org.apache.shiro.crypto.hash.Md5Hash.ALGORITHM_NAME;
/**
* @Author wangli
* @Date: 2022/3/11 23:51
* @Version 1.0
*/
@Component
public class MD5PasswordUtil {
//MD5加密密码
public String getEncryptionPass(String password){
SimpleHash encryptionPass = new SimpleHash(ALGORITHM_NAME, password, "salt", 3);
return encryptionPass.toString();
}
}
--扩展--
log4j2pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="OFF">
<appenders>
<Console name="Console" target="SYSTEM_OUT">
<!--只接受程序中DEBUG级别的日志进行处理-->
<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss.SSS}] %-5level %class{36} %L %M - %msg%xEx%n"/>
</Console>
<!--处理DEBUG级别的日志,并把该日志放到logs/debug.log文件中-->
<!--打印出DEBUG级别日志,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
<RollingFile name="RollingFileDebug" fileName="./logs/debug.log"
filePattern="logs/$${date:yyyy-MM}/debug-%d{yyyy-MM-dd}-%i.log.gz">
<Filters>
<!--只接受DEBUG级别的日志,其余的全部拒绝处理-->
<ThresholdFilter level="DEBUG"/>
<ThresholdFilter level="INFO" onMatch="DENY" onMismatch="NEUTRAL"/>
</Filters>
<!--输出日志的格式-->
<PatternLayout
pattern="[%d{yyyy-MM-dd HH:mm:ss}] %-5level %class{36} %L %M - %msg%xEx%n"/>
<Policies>
<SizeBasedTriggeringPolicy size="500 MB"/>
<TimeBasedTriggeringPolicy/>
</Policies>
</RollingFile>
<!--处理INFO级别的日志,并把该日志放到logs/info.log文件中-->
<RollingFile name="RollingFileInfo" fileName="./logs/info.log"
filePattern="logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log.gz">
<Filters>
<!--只接受INFO级别的日志,其余的全部拒绝处理-->
<ThresholdFilter level="INFO"/>
<ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL"/>
</Filters>
<PatternLayout
pattern="[%d{yyyy-MM-dd HH:mm:ss}] %-5level %class{36} %L %M - %msg%xEx%n"/>
<Policies>
<SizeBasedTriggeringPolicy size="500 MB"/>
<TimeBasedTriggeringPolicy/>
</Policies>
</RollingFile>
<!--处理WARN级别的日志,并把该日志放到logs/warn.log文件中-->
<RollingFile name="RollingFileWarn" fileName="./logs/warn.log"
filePattern="logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log.gz">
<Filters>
<ThresholdFilter level="WARN"/>
<ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/>
</Filters>
<PatternLayout
pattern="[%d{yyyy-MM-dd HH:mm:ss}] %-5level %class{36} %L %M - %msg%xEx%n"/>
<Policies>
<SizeBasedTriggeringPolicy size="500 MB"/>
<TimeBasedTriggeringPolicy/>
</Policies>
</RollingFile>
<!--处理error级别的日志,并把该日志放到logs/error.log文件中-->
<RollingFile name="RollingFileError" fileName="./logs/error.log"
filePattern="logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log.gz">
<ThresholdFilter level="ERROR"/>
<PatternLayout
pattern="[%d{yyyy-MM-dd HH:mm:ss}] %-5level %class{36} %L %M - %msg%xEx%n"/>
<Policies>
<SizeBasedTriggeringPolicy size="500 MB"/>
<TimeBasedTriggeringPolicy/>
</Policies>
</RollingFile>
<!--druid的日志记录追加器-->
<RollingFile name="druidSqlRollingFile" fileName="./logs/druid-sql.log"
filePattern="logs/$${date:yyyy-MM}/api-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss}] %-5level %L %M - %msg%xEx%n"/>
<Policies>
<SizeBasedTriggeringPolicy size="500 MB"/>
<TimeBasedTriggeringPolicy/>
</Policies>
</RollingFile>
</appenders>
<!-- 然后定义logger,只有定义了logger并引入的appender,appender才会生效 -->
<loggers>
<!--默认的root的logger-->
<root level="DEBUG">
<appender-ref ref="Console"/>
<appender-ref ref="RollingFileInfo"/>
<appender-ref ref="RollingFileWarn"/>
<appender-ref ref="RollingFileError"/>
<appender-ref ref="RollingFileDebug"/>
</root>
<!--额外配置的logger-->
<!--记录druid-sql的记录-->
<logger name="druid.sql.Statement" level="debug" additivity="false">
<appender-ref ref="druidSqlRollingFile"/>
</logger>
<!--log4j2 自带过滤日志-->
<Logger name="org.apache.catalina.startup.DigesterFactory" level="error" />
<Logger name="org.apache.catalina.util.LifecycleBase" level="error" />
<Logger name="org.apache.coyote.http11.Http11NioProtocol" level="warn" />
<logger name="org.apache.sshd.common.util.SecurityUtils" level="warn"/>
<Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="warn" />
<Logger name="org.crsh.plugin" level="warn" />
<logger name="org.crsh.ssh" level="warn"/>
<Logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="error" />
<Logger name="org.hibernate.validator.internal.util.Version" level="warn" />
<logger name="org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration" level="warn"/>
<logger name="org.springframework.boot.actuate.endpoint.jmx" level="warn"/>
<logger name="org.thymeleaf" level="warn"/>
</loggers>
</configuration>