导入依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.2</version>
</dependency>
shiro核心
主要功能
shiro主要有三大功能模块:
- Subject:主体,一般指用户。
- SecurityManager:安全管理器,管理所有Subject,可以配合内部安全组件。(类似于SpringMVC中的DispatcherServlet)
- Realms:用于进行权限信息的验证,一般需要自己实现。
细分功能
- Authentication:身份认证/登录(账号密码验证)。
- Authorization:授权,即角色或者权限验证。
- Session Manager:会话管理,用户登录后的session相关管理。
- Cryptography:加密,密码加密等。
- Web Support:Web支持,集成Web环境。
- Caching:缓存,用户信息、角色、权限等缓存到如redis等缓存中。
- Concurrency:多线程并发验证,在一个线程中开启另一个线程,可以把权限自动传播过去。
- Testing:测试支持;
- Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问。
- Remember Me:记住我,登录后,下次再来的话不用登录了。
新建MyRealm类
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
public class MyRealm extends AuthorizingRealm {
//权限认证
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
@Override
//登录认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
return null;
}
}
编写配置类
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
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.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager);
//拦截请求
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/manages/*","perms[admin:all]");
filterChainDefinitionMap.put("/user/login","anon");
bean.setLoginUrl("/user/toLogin");
bean.setUnauthorizedUrl("/user/unaUth");
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return bean;
}
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("myRealm") MyRealm myRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm);
return securityManager;
}
@Bean
public MyRealm myRealm(){
return new MyRealm();
}
@Bean(name = "hashedMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");// 散列算法:这里使用MD5算法;
hashedCredentialsMatcher.setHashIterations(2);// 散列的次数,比如散列两次,相当于md5(md5("")),进行两次md5加密;
//hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);// 表示是否存储散列后的密码为16进制,需要和生成密码时的一样,默认是base64;
return hashedCredentialsMatcher;
}
}
编写加密工具类
import org.apache.shiro.crypto.hash.SimpleHash;
//加密
public class ShiroEncryption {
/***
* 对用户的密码进行MD5加密
* 做成工具类
*/
public static String shiroEncryption(String password,String salt) {
// // shiro 自带的工具类生成salt
// salt = new SecureRandomNumberGenerator().nextBytes().toString();
// 加密次数
int times = 2;
// 算法名称
String algorithmName = "md5";
String encodedPassword = new SimpleHash(algorithmName,password,salt,times).toString();
// 返回加密后的密码
return encodedPassword;
}
// public static void main(String[] args) {
// System.out.println(ShiroEncryption.shiroEncryption("123456","wxy"));
// }
}
会话相关API
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession();
1
2
与web中的 HttpServletRequest.getSession(boolean create) 类似!
Subject.getSession(true)。即如果当前没有创建session对象会创建一个;
Subject.getSession(false),如果当前没有创建session对象则返回null。
拿到session之后,就可以调用以下API
返回值 方法名 描述
Object getAttribute(Object key) 根据key标识返回绑定到session的对象
Collection getAttributeKeys() 获取在session中存储的所有的key
String getHost() 获取当前主机ip地址,如果未知,返回null
Serializable getId() 获取session的唯一id
Date getLastAccessTime() 获取最后的访问时间
Date getStartTimestamp() 获取session的启动时间
long getTimeout() 获取session失效时间,单位毫秒
void setTimeout(long maxIdleTimeInMillis) 设置session的失效时间
Object removeAttribute(Object key) 通过key移除session中绑定的对象
void setAttribute(Object key, Object value) 设置session会话属性
void stop() 销毁会话
void touch() 更新会话最后访问时间
会话管理器
会话管理器管理着应用中所有Subject的会话的创建、维护、删除、失效、验证等工作。是Shiro的核心组件,顶层组件SecurityManager直接继承了SessionManager,且提供了SessionsSecurityManager实现直接把会话管理委托给相应的SessionManager,DefaultSecurityManager及DefaultWebSecurityManager默认SecurityManager都继承了SessionsSecurityManager。
SecurityManager提供了如下接口:
Session start(SessionContext context); //启动会话
Session getSession(SessionKey key) throws SessionException;//根据会话Key获取会话
另外用于Web环境的WebSessionManager又提供了如下接口:
boolean isServletContainerSessions();//是否使用Servlet容器的会话
Shiro还提供了ValidatingSessionManager用于验资并过期会话:
void validateSessions();//验证所有会话是否过期
Shiro提供了三个默认实现:
DefaultSessionManager:DefaultSecurityManager使用的默认实现,用于JavaSE环境;
ServletContainerSessionManager:DefaultWebSecurityManager使用的默认实现,用于Web环境,其直接使用Servlet容器的会话;
DefaultWebSessionManager:用于Web环境的实现,可以替代ServletContainerSessionManager,自己维护着会话,直接废弃了Servlet容器的会话管理。
import org.apache.shiro.session.Session;
import org.apache.shiro.session.SessionListener;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @description: 配置session监听器
*/
public class ShiroSessionListener implements SessionListener{
/**
* 统计在线人数
* juc包下线程安全自增
*/
private final AtomicInteger sessionCount = new AtomicInteger(0);
/**
* 会话创建时触发
* @param session
*/
@Override
public void onStart(Session session) {
//会话创建,在线人数加一
sessionCount.incrementAndGet();
}
/**
* 退出会话时触发
* @param session
*/
@Override
public void onStop(Session session) {
//会话退出,在线人数减一
sessionCount.decrementAndGet();
}
/**
* 会话过期时触发
* @param session
*/
@Override
public void onExpiration(Session session) {
//会话过期,在线人数减一
sessionCount.decrementAndGet();
}
/**
* 获取在线人数使用
* @return
*/
public AtomicInteger getSessionCount() {
return sessionCount;
}
}
在ShiroConfig类中添加以下Bean
配置session监听
/**
配置session监听
@return
/
@Bean(“sessionListener”)
public ShiroSessionListener sessionListener(){
ShiroSessionListener sessionListener = new ShiroSessionListener();
return sessionListener;
}配置会话ID生成器
/*
配置会话ID生成器
@return
*/
@Bean
public SessionIdGenerator sessionIdGenerator() {
return new JavaUuidSessionIdGenerator();
}
剩下部分参考此链接
https://blog.csdn.net/qq_34021712/article/details/80418112
MyRealm
import com.pzh.manage.domain.User;
import com.pzh.manage.service.UserService;
import com.pzh.manage.utils.ShiroEncryption;
import org.apache.shiro.SecurityUtils;
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.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// System.out.println("-----开始授权-----");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//获取当前登录的对象
Subject subject = SecurityUtils.getSubject();
//获取用户
User user = (User) subject.getPrincipal();
//获取角色信息
String role = userService.findRole(user.getUsername());
//角色判断
if("admin".equals(role)){
info.addRole(role);
//权限判断
if("admin:all".equals(userService.findPerm(role))){
info.addStringPermission("admin:all");
}
}
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// System.out.println("--------开始认证--------");
//拿到前端token
UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
//获取用户信息
User user = userService.findUserName(userToken.getUsername());
//获取用户输入的密码
String password = String.valueOf(userToken.getPassword());
try {
//账号认证
if(!userToken.getUsername().equals(user.getUsername())){
return null;
}else if(!ShiroEncryption.shiroEncryption(password,userToken.getUsername()).equals(user.getPassword())){
return null;
}
}catch (Exception e){
return null;
}
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user,userToken.getPassword(), ByteSource.Util.bytes(userToken.getUsername()),getName());
//密码认证
return simpleAuthenticationInfo;
}
}
controller
@PostMapping("/login")
public Boolean login(@RequestBody HashMap map){
Boolean flag = false;
//获取当前用户
Subject subject = SecurityUtils.getSubject();
//封装用户的数据
UsernamePasswordToken Token = new UsernamePasswordToken((String) map.get("username"),(String) map.get("password"));
try{
//开始登录
subject.login(Token);
flag=true;
}catch (UnknownAccountException uae){
}catch (IncorrectCredentialsException ice){
}catch (AuthenticationException ae){
}finally {
return flag;
}
}
//返回登录状态
@PostMapping("/loginStatus")
public Map<String,String> loginStatus(){
Map<String,String> map = new HashMap<>();
//1.获取当前用户
Subject subject = SecurityUtils.getSubject();
User user = (User) subject.getPrincipal();
map.put("username",user.getUsername());
if(subject.hasRole("admin")){
map.put("role","admin");
}else{
map.put("role","普通用户");
}
return map;
}
shiroconfig
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager);
//拦截请求
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/manages/*","perms[admin:all]");
filterChainDefinitionMap.put("/user/login","anon");
bean.setLoginUrl("/user/toLogin");
bean.setUnauthorizedUrl("/user/unaUth");
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return bean;
}
);
//拦截请求
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/manages/*","perms[admin:all]");
filterChainDefinitionMap.put("/user/login","anon");
bean.setLoginUrl("/user/toLogin");
bean.setUnauthorizedUrl("/user/unaUth");
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return bean;
}