1 需求01

  用户进行过认证登录后,某些接口是有权限限制的;如何实现只有相应权限的用户才可以调用相应接口

2 修改shiro配置类  ShiroConfiguration

package cn.xiangxu.apache_shiro;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; /**
* Shiro配置类
*/
@Configuration
public class ShiroConfiguration { @Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager); shiroFilterFactoryBean.setLoginUrl("/login"); // 定义登录的url
shiroFilterFactoryBean.setSuccessUrl("/index"); // 定义登录成功后的url
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized"); // 没有权限时跳转的url LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); // 请求拦截配置
filterChainDefinitionMap.put("/index", "authc");
filterChainDefinitionMap.put("/login", "anon"); // 排除 login 的验证
filterChainDefinitionMap.put("/loginUser", "anon"); // 排除 loginUser 的验证
filterChainDefinitionMap.put("/admin", "roles[admin]");
filterChainDefinitionMap.put("/**", "user"); // 所有请求都必须进行登录过滤
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean;
} @Bean
public SecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(authRealm);
return defaultWebSecurityManager;
} /** 注入自定义密码比较器 */
@Bean
public CredentialMather credentialMather() {
return new CredentialMather();
} /** 注入自定义的授权、认证登录类 */
@Bean
public AuthRealm authRealm(@Qualifier("credentialMather") CredentialMather credentialMather) {
AuthRealm authRealm = new AuthRealm();
authRealm.setCredentialsMatcher(credentialMather);
return authRealm;
} /** Shiro与Spring整合配置 */
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
} }

  

  代码解释:只用用户角色为 admin 的用户才可以访问 /admin 接口

  原理请参见 RolesAuthorizationFilter 源码

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package org.apache.shiro.web.filter.authz; import java.io.IOException;
import java.util.Set;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.CollectionUtils; public class RolesAuthorizationFilter extends AuthorizationFilter {
public RolesAuthorizationFilter() {
} public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
Subject subject = this.getSubject(request, response);
String[] rolesArray = (String[])((String[])mappedValue);
if (rolesArray != null && rolesArray.length != 0) {
Set<String> roles = CollectionUtils.asSet(rolesArray);
return subject.hasAllRoles(roles);
} else {
return true;
}
}
}

RolesAuthorizationFilter

3 修改授权、认证登录配置类 AuthRealm

package cn.xiangxu.apache_shiro;

import cn.xiangxu.apache_shiro.model.Permission;
import cn.xiangxu.apache_shiro.model.Role;
import cn.xiangxu.apache_shiro.model.User;
import cn.xiangxu.apache_shiro.service.UserService;
import org.apache.commons.collections.CollectionUtils;
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.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList;
import java.util.List;
import java.util.Set; /**
* 自定义授权、认证登录类
*/
public class AuthRealm extends AuthorizingRealm {
@Autowired
private UserService userService; // 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
User user = (User)principalCollection.fromRealm(this.getClass().getName()).iterator().next(); // 从session中获取用户信息
List<String> permissionList = new ArrayList<>(); // 用于存放用户的权限列表
List<String> roleNameList = new ArrayList<>(); // 用于存放用户角色名称
Set<Role> roleSet = user.getRoleSet(); // 从用户信息中获取用户角色
if (CollectionUtils.isNotEmpty(roleSet)) {
for (Role role : roleSet) {
roleNameList.add(role.getRname());
Set<Permission> permissionSet = role.getPermissionSet();
if (CollectionUtils.isNotEmpty(permissionSet)) {
for (Permission permission : permissionSet) {
permissionList.add(permission.getPname());
}
}
}
} SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermissions(permissionList);
info.addRoles(roleNameList); return info;
} // 认证登录(使用用户名和密码进行登录认证)
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken)authenticationToken;
String username = usernamePasswordToken.getUsername(); // 取出用户名
User user = userService.findByUsername(username); // 根据用户名到数据库中去获取用户信息
return new SimpleAuthenticationInfo(user, user.getPassword(), this.getClass().getName());
}
}

  

  代码解释:将用户的角色名称提取出来并添加到 SimpleAuthorizationInfo 对象中

4 需求02

  一个用户已经认证登录成功,根据他所拥有的权限来判定他可以访问那些接口

 

5 重写shiro配置类  ShiroConfiguration

package cn.xiangxu.apache_shiro;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; /**
* Shiro配置类
*/
@Configuration
public class ShiroConfiguration { @Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager); shiroFilterFactoryBean.setLoginUrl("/login"); // 定义登录的url
shiroFilterFactoryBean.setSuccessUrl("/index"); // 定义登录成功后的url
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized"); // 没有权限时跳转的url LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); // 请求拦截配置
filterChainDefinitionMap.put("/index", "authc");
filterChainDefinitionMap.put("/login", "anon"); // 排除 login 的验证
filterChainDefinitionMap.put("/loginUser", "anon"); // 排除 loginUser 的验证
filterChainDefinitionMap.put("/admin", "roles[admin]"); // 角色限定
filterChainDefinitionMap.put("/edit", "perms[edit]"); // 权限限定
filterChainDefinitionMap.put("/**", "user"); // 所有请求都必须进行登录过滤
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean;
} @Bean
public SecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(authRealm);
return defaultWebSecurityManager;
} /** 注入自定义密码比较器 */
@Bean
public CredentialMather credentialMather() {
return new CredentialMather();
} /** 注入自定义的授权、认证登录类 */
@Bean
public AuthRealm authRealm(@Qualifier("credentialMather") CredentialMather credentialMather) {
AuthRealm authRealm = new AuthRealm();
authRealm.setCredentialsMatcher(credentialMather);
return authRealm;
} /** Shiro与Spring整合配置 */
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
} }

  

  代码解释:只有拥有 edit 权限的用户才可以访问 /edit 接口

  原理请参见 PermissionsAuthorizationFilter 源码

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package org.apache.shiro.web.filter.authz; import java.io.IOException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.subject.Subject; public class PermissionsAuthorizationFilter extends AuthorizationFilter {
public PermissionsAuthorizationFilter() {
} public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
Subject subject = this.getSubject(request, response);
String[] perms = (String[])((String[])mappedValue);
boolean isPermitted = true;
if (perms != null && perms.length > 0) {
if (perms.length == 1) {
if (!subject.isPermitted(perms[0])) {
isPermitted = false;
}
} else if (!subject.isPermittedAll(perms)) {
isPermitted = false;
}
} return isPermitted;
}
}

PermissionsAuthorizationFilter

6 接口总汇

package cn.xiangxu.apache_shiro.controller;

import cn.xiangxu.apache_shiro.model.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpSession; //@RestController // 前后端分离时使用的注解
@Controller // 前后端不分离使用的注解
public class TestController { @RequestMapping(value = "/unauthorized")
public String unauthorized() {
return "unauthorized";
} @GetMapping("/login")
public String login() {
return "login";
} @GetMapping(value = "/index")
public String index() {
return "index";
} @GetMapping(value = "/logout")
public String logout() {
Subject subject = SecurityUtils.getSubject();
if (subject != null) {
subject.logout();
}
return "login";
} @GetMapping(value = "/admin")
@ResponseBody
public String admin() {
return "admin success";
} @GetMapping(value = "/edit")
@ResponseBody
public String edit() {
return "edit success";
} // 前后端不分离的写法
@PostMapping("/loginUser")
public String loginUser(
@RequestParam("username") String username,
@RequestParam("password") String password,
HttpSession session
) {
System.out.println("进入登录接口");
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
Subject subject = SecurityUtils.getSubject(); // 获取主体 // 认证登录逻辑(调用AuthRealm类的相关方法进行认证登录)
try {
subject.login(token); // 调用主体的login方法进行认证登录
User user = (User)subject.getPrincipal(); // 如果认证登录成功后就可以从主体中获取到数据库中主体对应的信息
session.setAttribute("user", user); // 将从数据库中获取到的主体信息放到session中,以便在权限验证的时候使用
return "index"; // 认证登录成功后就进入主页面
} catch (Exception e) {
e.printStackTrace();
return "login"; // 认证登录失败就进入登录页面
}
} // 前后端分离的写法
// @PostMapping("/loginUser")
// public String loginUser(
// @RequestBody User formUser,
// HttpSession session
// ) {
// System.out.println("进入登录接口" + formUser);
// UsernamePasswordToken token = new UsernamePasswordToken(formUser.getUsername(), formUser.getPassword());
// Subject subject = SecurityUtils.getSubject();
//
// try {
// System.out.println("进入捕获01");
// subject.login(token);
// User user = (User)subject.getPrincipal();
// System.out.println(user);
// session.setAttribute("user", user);
// System.out.println("进入捕获02");
// return "index"; // 热证登录成功就返回提示信息,在前端进行主页面跳转
// } catch (Exception e) {
// e.printStackTrace();
// return "login"; // 认证登录失败就返回提示信息,在前端进行登录页面跳转
// }
// }
}

7 开启Shiro缓存

  从 AuthorizingRealm 源码可以看出, AuthorizingRealm 可以依赖注入 CacheManager 来实现缓存

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package org.apache.shiro.cache; public interface CacheManager {
<K, V> Cache<K, V> getCache(String var1) throws CacheException;
}

  技巧:CacheManager有两个实现类 -> 一个是MemoryConstrainedCacheManager, 一个是AbstractCacheManager

  只需要在shiro配置类中注入authRealm这个bean时添加一行代码就可以开启shiro缓存功能

  

package cn.xiangxu.apache_shiro;

import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; /**
* Shiro配置类
*/
@Configuration
public class ShiroConfiguration { @Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager); shiroFilterFactoryBean.setLoginUrl("/login"); // 定义登录的url
shiroFilterFactoryBean.setSuccessUrl("/index"); // 定义登录成功后的url
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized"); // 没有权限时跳转的url LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); // 请求拦截配置
filterChainDefinitionMap.put("/index", "authc");
filterChainDefinitionMap.put("/login", "anon"); // 排除 login 的验证
filterChainDefinitionMap.put("/loginUser", "anon"); // 排除 loginUser 的验证
filterChainDefinitionMap.put("/admin", "roles[admin]"); // 角色限定
filterChainDefinitionMap.put("/edit", "perms[edit]"); // 权限限定
filterChainDefinitionMap.put("/**", "user"); // 所有请求都必须进行登录过滤
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean;
} @Bean
public SecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(authRealm);
return defaultWebSecurityManager;
} /** 注入自定义密码比较器 */
@Bean
public CredentialMather credentialMather() {
return new CredentialMather();
} /** 注入自定义的授权、认证登录类 */
@Bean
public AuthRealm authRealm(@Qualifier("credentialMather") CredentialMather credentialMather) {
AuthRealm authRealm = new AuthRealm();
authRealm.setCacheManager(new MemoryConstrainedCacheManager()); // 开启内存缓存
authRealm.setCredentialsMatcher(credentialMather);
return authRealm;
} /** Shiro与Spring整合配置 */
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
} }

  

package com.mmall.demo2;

import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor; import javax.sql.DataSource; @Configuration
public class DruidConfiguration { @Bean
public ServletRegistrationBean statViewServlet() {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
//白名单:
servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
//IP黑名单 (存在共同时,deny优先于allow) : 如果满足deny的即提示:Sorry, you are not permitted to view this page.
servletRegistrationBean.addInitParameter("deny", "192.168.1.100");
//登录查看信息的账号密码.
servletRegistrationBean.addInitParameter("loginUsername", "druid");
servletRegistrationBean.addInitParameter("loginPassword", "12345678");
//是否能够重置数据.
servletRegistrationBean.addInitParameter("resetEnable", "false");
return servletRegistrationBean;
} @Bean
public FilterRegistrationBean statFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
//添加过滤规则.
filterRegistrationBean.addUrlPatterns("/*");
//添加不需要忽略的格式信息.
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistrationBean;
} @Bean
PersistenceExceptionTranslationPostProcessor persistenceExceptionTranslationPostProcessor() {
return new PersistenceExceptionTranslationPostProcessor();
} //配置数据库的基本链接信息
@Bean(name = "dataSource")
@Primary
@ConfigurationProperties(prefix = "spring.datasource") //可以在application.properties中直接导入
public DataSource dataSource() {
return DataSourceBuilder.create().type(com.alibaba.druid.pool.DruidDataSource.class).build();
} @Bean
public SqlSessionFactoryBean sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
bean.setMapperLocations(resolver.getResources("classpath:/mappers/*.xml"));
return bean;
}
}

druid配置类

    

最新文章

  1. cmd连接mysql的方法详解(转载)
  2. Android之TabActivity的使用
  3. python 模拟登录--day1
  4. Python 打开目录与指定文件
  5. windows下使用体验更好的控制台——ConsoleZ
  6. 在 ServiceModel 客户端配置部分中,找不到引用协定“WebServiceTest.WebServiceSoap”的默认终结点元素。这可能是因为未找到应用程序的配置文件,或者是因为客户端元素
  7. Fresco 源码分析(三) Fresco服务端处理(1) ImagePipeline为何物
  8. [RM HA3] Zookeeper在RM HA的应用
  9. 使用AndroidStudio dump heap,再用 Eclipse MAT插件分析内存泄露
  10. spring--mybatis事务总结
  11. engine的工具中实现Snapping(捕捉)
  12. PCB板上镀镍厚度
  13. 基于PCA的人脸识别步骤
  14. sina微博上看到的关于android界面设计相关的规范
  15. Linux指令--head,tail
  16. pwn-ROP(2)
  17. 比较好用的Copy代码到博客VS扩展工具
  18. BZOJ3253 : 改编
  19. ubuntu 增加一个用户 并赋予权限
  20. SpringBoot document notes

热门文章

  1. Python 不可变对象
  2. CKeditor插件开发流程(二)SyntaxHighlighter
  3. 【leetcode刷题笔记】Word Search
  4. STM32大文件分块校验CRC
  5. Java Applet:练习TextField类和TextArea类的文本事件
  6. 用python实现的抓取腾讯视频所有电影的爬虫
  7. webpack 教程资源目录
  8. for循环中删除map中的元素,valgrind检测提示error:Invalid read of size 8
  9. 机器学习(十七)— SVD奇异值分解
  10. c++能过,g++过不了