doGetAuthorizationInfo和doGetAuthenticationInfo
这两个方法虽然名字很像,但是意义是不一样的,doGetAuthorizationInfo方法是进行权限验证,doGetAuthenticationInfo是进行身份验证的(登录验证)
Realm就是负责验证的东西,可以是HashMap,SimpleAccountRealm,ini文件,数据库等 最常用的还是数据库.
简单验证
1.构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager();
2.为SecurityManager设置检测源
defaultSecurityManager.setRealm(simpleAccountRealm);
3.设置SecurityManager
SecurityUtils.setSecurityManager(defaultSecurityManager);
4.获得用户主体
Subject subject=SecurityUtils.getSubject();
5.根据用户密码获得token
UsernamePasswordToken token=new UsernamePasswordToken("Kenny","123456");
6.根据登陆
subject.login(token);
7.检测是否登陆成功和权限问题
subject.isAuthenticated() return boolean
subject.checkRole("admin");检测角色
subject.checkPermission("user:update");检测权限
通过INI文件验证
IniRealm iniRealm=new IniRealm("classpath:user.ini");
ini文件内容如下:
[users]
Kenny=111,admin
[roles]
admin=user:delete,user:update
剩下的和之前一样
通过数据库验证
1.初始化JdbcRealm
JdbcRealm jdbcRealm=new JdbcRealm();
2.为JdbcRealm设置数据源
jdbcRealm.setDataSource(druidDataSource);
3.设置自定义sql语句查询是否有该用户
String sql="select password from test_user where username=?";
jdbcRealm.setAuthenticationQuery(sql);
4.设置自定义sql语句查询是否有该角色
String sql="select password from test_user where username=?";
String roleSql="select role_name from test_user_role where user_name=?";
jdbcRealm.setUserRolesQuery(roleSql);
后面都一致
Shiro框架结合Springboot2.X
1.在pom.xml文件中导入必要的依赖
spring-boot-starter-web
spring-boot-starter-test
spring-boot-starter-thymeleaf
shiro-core
shiro-spring
mysql-connector-java
druid(阿里巴巴的)
下面两个是打开aop,实现注解管理权限功能
spring-boot-starter-aop
aspectjweaver
redis所需要的依赖(在日常开发问题文章中有写)
fastjson
2.设置自己的Realm
package com.kenny.shiroweb.realm;
import com.kenny.shiroweb.dao.UserDao;
import com.kenny.shiroweb.dao.UserRoleDao;
import com.kenny.shiroweb.vo.User;
import com.kenny.shiroweb.vo.UserRole;
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.crypto.hash.Md5Hash;
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.stereotype.Component;
import java.util.*;
@Component
public class CustomRealmextends AuthorizingRealm {
MapuserMap=new HashMap(16);
{
userMap.put("Kenny","672332263137c302ab54e66efd791c1b");
super.setName("customRealm");
}
@Autowired
private UserDaouserDao;
@Autowired
private UserRoleDaoroleDao;
protected AuthorizationInfodoGetAuthorizationInfo(PrincipalCollection principalCollection) {
String userName= (String) principalCollection.getPrimaryPrincipal();
//从数据库或者缓存中获取角色数据
Set roles=getRolesByUserName(userName);
Set permissions=getPermissionsByUserName(userName);
SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();
simpleAuthorizationInfo.setRoles(roles);
simpleAuthorizationInfo.setStringPermissions(permissions);
return simpleAuthorizationInfo;
}
protected AuthenticationInfodoGetAuthenticationInfo(AuthenticationToken authenticationToken)throws AuthenticationException {
//1.从主体传来得认证信息中,获得用户名
String userName=(String)authenticationToken.getPrincipal();
//2.通过用户名到数据库中获取凭证
String password=getPassworkByUserName(userName);
if(password==null){
return null;
}
SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(userName,password,"customRealm");
authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(userName));
return authenticationInfo;
}
private SetgetPermissionsByUserName(String userName) {
Set sets=new HashSet();
sets.add("user:delete");
sets.add("user:add");
return sets;
}
private SetgetRolesByUserName(String userName) {
System.out.println("从数据库中获取授权数据");
List list=roleDao.findByUsername(userName);
List role=new ArrayList<>();
for(UserRole userRole:list){
role.add(userRole.getRolename());
}
Set sets=new HashSet<>(role);
return sets;
}
private StringgetPassworkByUserName(String userName) {
List user=userDao.findByUsername(userName);
if(user!=null){
return user.get(0).getPassword();
}
return null;
}
public static void main(String[] args){
Md5Hash md5Hash=new Md5Hash("123","viewer");
System.out.println(md5Hash.toString());
}
}
密码加密方式是通过md5加盐(用户名)的形式进行加密
2.进行shiro配置
package com.kenny.shiroweb.config;
import com.kenny.shiroweb.cache.RedisCacheManager;
import com.kenny.shiroweb.filter.RolesOrFilter;
import com.kenny.shiroweb.realm.CustomRealm;
import com.kenny.shiroweb.session.CustomSessionManger;
import com.kenny.shiroweb.session.RedisSessionDao;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
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 javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBeanshirFilter(SecurityManager securityManager) {
System.out.println("ShiroConfiguration.shirFilter()");
ShiroFilterFactoryBean shiroFilterFactoryBean =new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//自定义拦截器
Map filtersMap =new LinkedHashMap();
filtersMap.put("rolesOrFilter", new RolesOrFilter());
shiroFilterFactoryBean.setFilters(filtersMap);
//拦截器.
Map filterChainDefinitionMap =new LinkedHashMap();
// 配置不会被拦截的链接 顺序判断
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/subLogin", "anon");
filterChainDefinitionMap.put("/isLogin", "anon");
//测试权限访问
filterChainDefinitionMap.put("/testConfigRole", "rolesOrFilter[user,admin]");
//配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
filterChainDefinitionMap.put("/logout.html", "logout");
//:这是一个坑呢,一不小心代码就不好使了;
//
filterChainDefinitionMap.put("/**", "authc");
// 如果不设置默认会自动寻找Web工程根目录下的"/login.html"页面
shiroFilterFactoryBean.setLoginUrl("/login.html");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/index.html");
//未授权界面;
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
//选择加密方式
@Bean
public HashedCredentialsMatcherhashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher =new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
hashedCredentialsMatcher.setHashIterations(1);//散列的次数,比如散列两次,相当于 md5(md5(""));
return hashedCredentialsMatcher;
}
//将自定义的reamlm注册为bean,并通过hashedCredentialsMatcher进行加密
@Bean
public CustomRealmcustomRealm(){
CustomRealm customRealmRealm =new CustomRealm();
customRealmRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return customRealmRealm;
}
/**
* 项目自定义的DAO
*/
@Bean
public RedisSessionDaoredisSessionDao() {
return new RedisSessionDao();
}
/**
* 自定义的CacheManager
* @return
*/
@Bean
public RedisCacheManagerredisCacheManager() {
return new RedisCacheManager();
}
//自定义sessionmanager
@Bean("sessionManager")
public CustomSessionMangersessionManager() {
CustomSessionManger sessionManager =new CustomSessionManger();
sessionManager.setSessionDAO(redisSessionDao());
return sessionManager;
}
/**
* 设置记住我的cookie
* @return
*/
@Bean
public SimpleCookierememberMeCookie(){
//System.out.println("ShiroConfiguration.rememberMeCookie()");
//这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
SimpleCookie simpleCookie =new SimpleCookie("rememberMe");
//
simpleCookie.setMaxAge(259200);
return simpleCookie;
}
/**
* cookie管理对象;
* rememberMeManager()方法是生成rememberMe管理器,而且要将这个rememberMe管理器设置到securityManager中
* @return
*/
@Bean
public CookieRememberMeManagerrememberMeManager(){
//System.out.println("ShiroConfiguration.rememberMeManager()");
CookieRememberMeManager cookieRememberMeManager =new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
//rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
cookieRememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag=="));
return cookieRememberMeManager;
}
@Bean
public DefaultWebSecurityManagersecurityManager(){
DefaultWebSecurityManager securityManager =new DefaultWebSecurityManager();
securityManager.setRealm(customRealm());
securityManager.setSessionManager(sessionManager());
securityManager.setCacheManager(redisCacheManager());
securityManager.setRememberMeManager(rememberMeManager());
return securityManager;
}
/**
* Shiro生命周期处理器
* @return
*/
@Bean
public LifecycleBeanPostProcessorlifecycleBeanPostProcessor(){
return new LifecycleBeanPostProcessor();
}
/**
* 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
* 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
* @return
*/
@Bean
@DependsOn({"lifecycleBeanPostProcessor"})
public DefaultAdvisorAutoProxyCreatoradvisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator =new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
@Bean
public AuthorizationAttributeSourceAdvisorauthorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
return authorizationAttributeSourceAdvisor;
}
}
还有redis的配置文件不在这写了
通过注解在控制层进行拦截
//必须要admin角色才能访问
@RequiresRoles("admin")
//要求subject中必须同时含有file:read和write:aFile.txt的权限才能执行方法
@RequiresPermissions({"user:delete", "user:add"} )
自定义拦截器
创建一个类继承AuthorizationFilter并重新他的isAccessAllowed方法,然后在ShiroConfig文件中配置
//自定义拦截器
Map filtersMap =new LinkedHashMap();
filtersMap.put("rolesOrFilter", new RolesOrFilter());
shiroFilterFactoryBean.setFilters(filtersMap);
自定义拦截器代码
package com.kenny.shiroweb.filter;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class RolesOrFilterextends AuthorizationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o)throws Exception {
Subject subject=getSubject(servletRequest,servletResponse);
String[] roles=(String[])o;
if(roles==null||roles.length==0){
return true;
}
for (String role : roles){
if(subject.hasRole(role)){
return true;
}
}
return false;
}
}
在ShiroConfig中使用:
filterChainDefinitionMap.put("/testConfigRole", "rolesOrFilter[user,admin]");
自定义SessionManger
在ShiroConfig中注册为bean
package com.kenny.shiroweb.session;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.session.mgt.WebSessionKey;
import javax.servlet.ServletRequest;
import java.io.Serializable;
public class CustomSessionMangerextends DefaultWebSessionManager {
@Override
protected SessionretrieveSession(SessionKey sessionKey)throws UnknownSessionException {
Serializable sessionId=getSessionId(sessionKey);
ServletRequest request=null;
if(sessionKeyinstanceof WebSessionKey){
request=((WebSessionKey)sessionKey).getServletRequest();
}
if(request!=null&&sessionId!=null){
Session session=(Session)request.getAttribute(sessionId.toString());
if(session!=null){
return session;
}
}
Session session=super.retrieveSession(sessionKey);
if(request!=null&&sessionId!=null){
request.setAttribute(sessionId.toString(),session);
}
return session;
}
}
自定义SessionDao
新建一个类继承AbstractSessionDAO并重写他的方法
主要还是增删改查
key以kenny-session:为前缀加上sessionid
value:为session的序列化
因为redis中不能直接存储对象,需要将他序列化SerializationUtils.serialize(session);
当需要从redis中取出来的时候再反序列化强转为Session对象即可.
然后将自定义的SessionDao注册为Bean,
在刚自定义的CustomSessionManger添加自定义的DAO
sessionManager.setSessionDAO(redisSessionDao());
自定义CacheManager
新建一个类并实现CacheManager接口
还是通过redis进行缓存,与SessionManager类似,只是存储的对象不一样
将刚自定义的redisCacheManager在ShiroConfig中注册为bean
记住我功能
在ShrioConfig文件中添加两个Bean
/**
* 设置记住我的cookie
* @return
*/
@Bean
public SimpleCookierememberMeCookie(){
//System.out.println("ShiroConfiguration.rememberMeCookie()");
//这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
SimpleCookie simpleCookie =new SimpleCookie("rememberMe");
//
simpleCookie.setMaxAge(259200);
return simpleCookie;
}
/**
* cookie管理对象;
* rememberMeManager()方法是生成rememberMe管理器,而且要将这个rememberMe管理器设置到securityManager中
* @return
*/
@Bean
public CookieRememberMeManagerrememberMeManager(){
//System.out.println("ShiroConfiguration.rememberMeManager()");
CookieRememberMeManager cookieRememberMeManager =new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
//rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
cookieRememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag=="));
return cookieRememberMeManager;
}
在securityManager中设置这个管理器
securityManager.setRememberMeManager(rememberMeManager());
在控制层上通过
token.setRememberMe(user.isRememberMe());来决定是否开启这个功能