shiro权限控制
1.权限控制
1.1 什么是权限控制
白话文:控制某某用户可以/不可以访问某某功能。
我们今天的权限控制,实际上就是对资源加锁,每个锁都有一些规则,只有对应的钥匙才能打开,我们根据用户的角色,将钥匙分配给不同用户。
1.2 权限控制的方式
1、基于过滤器进行控制。比如我们servlet中的filter
2、基于注解进行控制。可以借助spring的AOP进行控制。
3、直接在业务代码中判断(不可取)
4、页面标签的权限控制——不太常用。
2.shiro简介
2.1 功能
认证:即我们常说的登录
授权:给予用户资源权限
会话:当前认证用户会话
缓存:缓存认证权限等
2.2 shiro核心流程
图示
——这个图只要是用shiro必定逃离不了。
2.3 权限控制方式:
filter方式
//设置过滤匹配路径和判断规则
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
filterChainDefinitionMap.put("/**", "anon");//可以匿名访问
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
其中,shiro的filter列表,盗了一张图:
过滤器别名 | 对应的过滤器 |
anon | org.apache.shiro.web.filter.authc.AnonymousFilter |
authc | |
authcBasic | org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter |
logout | |
noSessionCreation | |
perms | org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter |
port | |
rest | org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter |
roles | |
ssl | |
user |
注解方式
//在具体的方法上添加注解
@RequiresAuthentication :表示当前Subject已经认证:Subject.isAuthenticated()返回true
@RequiresUse:表示当前Subject已经身份验证或者通过记住我登录的
@RequiresGuest :表示当前Subject没有身份验证或通过记住我登录过,即是游客身份
@RequiresRoles(value={“admin”, “user”}, logical= Logical.AND)
@RequiresPermissions(value={“user:select”, “user:all”}, logical= Logical.OR)
只是用注解是不生效的,需要添加配置
/**
* 注解支持:
*/
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
标签(了解)
注意,此处使用的是thymeleaf做视图界面展示,并非jsp
<!--输出当前用户信息,通常为登录帐号信息。如果存入的principal是对象,可以通过property取值-->
<span> <shiro:principal property="username"/> </span>
<!--验证当前用户是否拥有指定权限。 -->
<a shiro:hasPermission="user:select" href="#" >查看用户列表</a><!-- 拥有权限 -->
<!--与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过。-->
<p shiro:lacksPermission="user:del"> 没有权限 </p>
<!--验证当前用户是否拥有以下所有权限。-->
<p shiro:hasAllPermissions="user:view, user:add">拥有所有才能访问</p>
<!--验证当前用户是否拥有以下任意一个权限。-->
<p shiro:hasAnyPermissions="user:view, user:del">拥有任意一个即可访问</p>
<!--验证当前用户是否属于该角色。-->
<a shiro:hasRole="role_admin" href="#">是role_admin角色</a>
<!--与hasRole标签逻辑相反,当用户不属于该角色时验证通过。-->
<p shiro:lacksRole="role_admin">我没有role_admin角色</p>
<!--验证当前用户是否属于以下所有角色。-->
<p shiro:hasAllRoles="developer, admin"> 角色与判断 </p>
<!--验证当前用户是否属于以下任意一个角色。-->
<p shiro:hasAnyRoles="admin, vip, developer"> 角色或判断 </p>
<!--验证当前用户是否为“访客”,即未认证(包含未记住)的用户。-->
<p shiro:guest="">访客 未认证</a></p>
<!--已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在。-->
<p shiro:authenticated=""> <span shiro:principal=""></span> </p>
<!--未认证通过用户,与authenticated标签相对应。-->
<!--与guest标签的区别是,该标签包含已记住用户。-->
<p shiro:notAuthenticated=""> 未认证通过用户 </p>
3.springboot整合shiro
3.1 SecurityManager
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
List<Realm> realms = new ArrayList<Realm>();
realms.add(customRealm());
securityManager.setRealms(realms);
securityManager.setSessionManager(sessionManager());
return securityManager;
}
3.2 Realm
//主要用作封装认证、授权信息,返回给安全管理器处理
@Bean
public CustomRealm customRealm(){
CustomRealm customRealm = new CustomRealm();
//设置密码校验算法——需要和加密算法一致
customRealm.setCredentialsMatcher(credentialsMatcher());
return customRealm;
}
@Bean
public CredentialsMatcher credentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
// hashedCredentialsMatcher.setHashIterations(2);
return hashedCredentialsMatcher;
}
3.3 ShiroFilter
@Bean
public ShiroFilterFactoryBean shiroFilter2(SecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("/admin/toPage");
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
//访问控制
filterChainDefinitionMap.put("/admin/toPage", "anon");//可以匿名访问
filterChainDefinitionMap.put("/admin/login", "anon");//可以匿名访问
filterChainDefinitionMap.put("/storage/list", "perms[user:select]");//可以匿名访问
// filterChainDefinitionMap.put("/", "anon");//可以匿名访问
filterChainDefinitionMap.put("/**", "anon");//可以匿名访问
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
3.4 SessionManager
@Bean
public SessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(redisSessionDAO());
//设置会话过期时间
sessionManager.setGlobalSessionTimeout(3*60*1000); //默认半小时
sessionManager.setDeleteInvalidSessions(true); //默认自定调用SessionDAO的delete方法删除会话
//设置会话定时检查
sessionManager.setSessionValidationInterval(180000); //默认一小时
sessionManager.setSessionValidationSchedulerEnabled(true);
return sessionManager;
}
@Bean
public SessionDAO redisSessionDAO(){
ShiroSessionRedisDAO redisDAO = new ShiroSessionRedisDAO();
return redisDAO;
}
3.4 用户新增
在controller中接收新增用户后,交给service的saveAdmin做密码加密后保存
public void saveAdmin(Administrator admin) {
//对密码进行加密处理
//1.得到明文密码
String password = admin.getPassword();
//2.生成随机盐 4-8位的随机数字
String randomSalt = RandomStringUtils.randomNumeric(4, 8);
admin.setPrivateSalt(randomSalt);
//3.md5加密 添加随机盐,加密次数1次(当然,这个一次可以配置在配置文件在,此处注入读取)
Md5Hash md5Hash = new Md5Hash(password,randomSalt,1);
String hashPwd = md5Hash.toString();
admin.setPassword(hashPwd);
userMapper.insert(admin);
}
3.5 用户登录
认证
@Controller
@RequestMapping("/admin")
public class LoginController {
@PostMapping("/login")
public String login(
@RequestParam("username") String username,
@RequestParam("password") String password,Model map){
try {
Subject subject = SecurityUtils.getSubject();
//处理登录的用户名密码token
UsernamePasswordToken token =
new UsernamePasswordToken(username, password);
//登录(开始认证)
subject.login(token);
return "redirect:/admin/main";
}catch (UnknownAccountException ukactException){
//用户不存在异常
ukactException.printStackTrace();
map.addAttribute("err_msg","用户不存在");
return "login";
}catch (IncorrectCredentialsException credentialExctption){
credentialExctption.printStackTrace();
map.addAttribute("err_msg","密码错误");
return "login";
}catch (Exception e){
e.printStackTrace();
map.addAttribute("err_msg","服务器错误,请重试");
return "login";
}
}
}
3.6 用户访问(权限控制)
4.其他
4.1 thymeleaf中使用shiro标签的支持
依赖坐标:
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
配置支持:
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
4.2 request判断ajax请求
//通过头X-Requested-With=XMLHttpRequest来判断
request.getHeader("X-Requested-With");