1、依赖
<!-- shiro核心包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.5.3</version>
</dependency>
<!-- shiro与redis整合 -->
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>3.2.3</version>
</dependency>
<!-- shiro与spring整合 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.3</version>
</dependency>
2、配置
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/shiro?characterEncoding=utf-8
username: root
password: 123456
jpa:
show-sql: true
hibernate:
ddl-auto: update
redis:
host: xxx.xxx.xxx.xxx
port: 6379
3、流程
1、配置Controller
用户输入用户名和密码后发送login请求给后端,后端接收到用户名和密码后使用SecurityUtils获取Subject,初始化一个UserPasswordToken将用户名和密码放入,调用subject.login方法。
2、自定义Realm
自定义Realm集成AuthorizingRealm,重写doGetAuthorizationInfo和doGetAuthenticationInfo以及setName方法,在setName方法里设置自定义Realm的名字。在doGetAuthenticationInfo进行认证处理,在doGetAuthorizationInfo进行授权处理。
package com.wx.springboot.realm;
import com.wx.springboot.entity.Permission;
import com.wx.springboot.entity.Role;
import com.wx.springboot.entity.User;
import com.wx.springboot.repository.UserRepository;
import com.wx.springboot.utils.Constants;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
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.util.StringUtils;
import java.util.Set;
/**
* Description:
* Author:逗你妹
* Date:2020/5/16
*/
public class CustomeRealm extends AuthorizingRealm {
@Autowired
UserRepository userRepository;
@Override
public void setName(String name) {
super.setName("customeRealm");
}
/**
* @Description:授权
* @Date: 2020/5/16
**/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
User user = (User)principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Set<Role> roles = user.getRoles();
for (Role role : roles){
info.addRole(role.getName());
for (Permission perm : role.getPermissions()){
info.addStringPermission(perm.getName());
}
}
return info;
}
/**
* @Description:认证
* @Date: 2020/5/16
**/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) {
UsernamePasswordToken upToken = (UsernamePasswordToken)authenticationToken;
String username = upToken.getUsername();
User user = userRepository.findUserByUsername(username);
if(!StringUtils.isEmpty(user)){
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());
// 加盐
info.setCredentialsSalt(ByteSource.Util.bytes(username));
return info;
}else{
return null;
}
}
}
3、配置Shiro配置文件
package com.wx.springboot.config;
import com.wx.springboot.realm.CustomeRealm;
import com.wx.springboot.utils.Constants;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
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.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
/**
* Description:
* Author:逗你妹
* Date:2020/5/17
*/
@Configuration
public class ShiroConfiguration {
private String host = "xxx.xxx.xxx.xxx:6379";
private String password = "xxxxxx";
// 配置自定义的Realm
@Bean
public CustomeRealm getRealm(){
CustomeRealm customeRealm = new CustomeRealm();
// 配置凭证匹配器
customeRealm.setCredentialsMatcher(getHashCredential());
return customeRealm;
}
// 配置安全管理器
@Bean
public SecurityManager getSecurityManager(CustomeRealm realm){
DefaultSecurityManager securityManager = new DefaultWebSecurityManager(realm);
// 设置自定义session管理
securityManager.setSessionManager(sessionManager());
// 设置自定义缓存
securityManager.setCacheManager(cacheManager());
// 设置自定义Realm
securityManager.setRealm(realm);
return securityManager;
}
// 设置凭证匹配器
@Bean
public HashedCredentialsMatcher getHashCredential(){
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName(Constants.ALGORITHNAME);
matcher.setHashIterations(Constants.HASHINTERATIONS);
matcher.setStoredCredentialsHexEncoded(true);
return matcher;
}
// Filter工厂,设置对应的过滤条件和跳转请求
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
// 创建Shiro过滤器工厂
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
// 设置安全管理器
factoryBean.setSecurityManager(securityManager);
// 通用配置(设置登录页面,成功页面,失败页面)
factoryBean.setLoginUrl("/login/authLogin?code=1");
factoryBean.setUnauthorizedUrl("/login/authLogin?code=2");
// 设置过滤器集合
/**
* key:访问链接
* 支持通配符
* value:过滤器类型
* shiro常用类型
* anon:匿名访问(所有人都可以访问)
* authc:认证后访问(需要先登录成功后才可以访问)
*/
LinkedHashMap<String, String> filterMap = new LinkedHashMap<>();
// filterMap.put("/home/page", "perms[home:page]");
// filterMap.put("/home/page", "roles[role1]");
filterMap.put("/login/login", "anon");
filterMap.put("/home/page", "authc");
filterMap.put("/user/**", "authc");
factoryBean.setFilterChainDefinitionMap(filterMap);
return factoryBean;
}
// 1.配置Shiro RedisManager 管理器
public RedisManager redisManager(){
RedisManager redisManager = new RedisManager();
redisManager.setHost(host);
redisManager.setPassword(password);
return redisManager;
}
// 2.配置Shiro缓存机制 使用Redis实现
public RedisCacheManager cacheManager(){
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
}
// 3.配置SessionDao
public RedisSessionDAO redisSessionDAO(){
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager());
return redisSessionDAO;
}
// 4.配置会话管理器,指定sessionDao的依赖关系
public DefaultWebSessionManager sessionManager(){
CustomSessionManager customSessionManager = new CustomSessionManager();
customSessionManager.setSessionDAO(redisSessionDAO());
return customSessionManager;
}
// 配置Shiro注解支持
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
4、自定义Session管理器
package com.wx.springboot.config;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.util.StringUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;
/**
* Description:自定义session管理器
* Author:逗你妹
* Date:2020/5/17
*/
public class CustomSessionManager extends DefaultWebSessionManager {
private static final String AUTHORIZATION = "Authorization";
private static final String SESSION_ID_SOURCE = "header";
@Override
protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
if(!StringUtils.isEmpty(id)){
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, 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);
}
}
}
5、配置全局异常类处理
package com.wx.springboot.config;
import com.wx.springboot.constant.Result;
import com.wx.springboot.constant.ResultCode;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.naming.AuthenticationException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Description:
* Author:逗你妹
* Date:2020/5/16
*/
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
@ExceptionHandler(value = {AuthenticationException.class, AuthorizationException.class})
public Result exceptionHandler(HttpServletRequest request, HttpServletResponse response,Exception e){
if(e instanceof AuthorizationException){
// 未登录
return new Result(ResultCode.UNAUTHORISE);
}else if(e instanceof AuthenticationException){
// 未授权
return new Result(ResultCode.UNAUTHENTICATED);
}else{
return new Result(ResultCode.SERVERERROR);
}
}
}
4、Shiro常用注解和权限
在Controller方法上或者类上可以根据角色和权限进行注解设置。
角色:@RequiresRoles(value = {"xxx"}):需要xxx角色才可以访问类或方法
权限:@RequiresPermissions(value = {"xxx"}):需要xxx权限才可以访问类或方法