Shiro安全框架基础入门使用

发布时间:2024年01月03日

Shiro的核心功能

  • Authentication 认证登录
  • Authorization 授权、权限验证
  • Session Management 会话管理
  • Cryptography 加/解密

Shiro 的核心架构

  • Authenticator 认证器
  • Authorizer 授权器
  • Session Manager 会话管理器
  • Cache Manager 缓存控制器
  • Session DAO 会话数据访问接口
  • Realm 连接数据源,提供认证,授权、令牌等信息
  • Cryptography 加/解密

SpringBoot整合Shiro

<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-spring-boot-web-starter</artifactId>
	<version>1.9.0</version>
<dependency>

配置文件

  • shiro.ini
  • application.yml shiro:

登录认证:

  • 自定义Bean类型MyRealm继承自AuthorizingRealm类,并重写实现doGetAuthenticationInfo(AuthenticationToken authenticationToken)方法
  • 获取用户身份信息
    • String name = authenticationToken.getPrincipal().toString();
  • 业务层查询数据库获取用户信息
    • User user = userService.getUserInfoByName(name);
  • 非空判断,将数据封装返回 if(user!=null){
    • AuthenticationInfo info = new SimpleAuthenticationInfo(authenticationToken.getPrncipal,user.getPwd(),ByteSource.Util.bytes(“salt值”),authenticationToken.getPrincipal.toString());
  • return info;
public class MyRealm extends AuthorizingRealm{
	@Autowired
	private UserService userService;
	//自定义登录认证方法
	@override
	public AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken){
		 String name = authenticationToken.getPrincipal().toString();
		 User user =  userService.getUserInfoByName(name);
		 if(user!=null){
		  //ByteSource.Util.bytes("salt值"),密码明文加密时拼接的盐值
		 AuthenticationInfo info = new SimpleAuthenticationInfo(authenticationToken.getPrncipal,user.getPwd(),ByteSource.Util.bytes("salt值"),authenticationToken.getPrincipal.toString());
		 return info;
		 }
	}
}

配置类ShiroConfig:

@Configuration
public class ShiroConfig {
	@Autowired
	private MyRealm myRealm;
	
	//配置SecurityManager,加密与密码匹配
	@Bean
	public DefaultWebSecurityManager defaultWebSecurityManager(){
		//1创建defaultWebSecurityManager 对象
		DefaultWebSecurityManager dwsm = new DefaultWebSeccurityManager();
		//2创建加密对象,设置相关属性
		HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
		//2.1 采用MD5算法加密
		matcher.setHashAlgorithmName("md5);
		//2.2 迭代加密次数
		matcher.setHashIterations(3);
		//3将加密对象存储到myRealm中,用于比对用户输入的密码与存储的是否一致
		myRealm.setCredentialsMatcher(matcher);
		//4将myRealm存入defaultWebSecurityManager对象
		dwsm.setRealm(myRealm);
		//5返回
		return dwsm;
	}
	
	//配置Shiro内置过滤器拦截范围
	@Bean
	public DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){
		DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
		//配置不认证可以访问的资源 "anon",匿名用户,表示不需要认证
		definition.addPathDefinition("/myController/userLogin","anon");
		definition.addPathDefinition("/login","anon");
		//配置需要进行登录认证的拦截范围 "authc",需要进行身份验证的用户,表示需要进行登录认证。
		definition.addPathDefinition("/**","authc");
		// 通过rememberMe不拦截,"user",已登录的用户,表示允许通过 rememberMe 功能进行身份验证的用户。
		definition.addPathDefinition("/**","user");
		// 配置登出,"logout",登出过滤器,表示用于处理用户登出的请求。
		definition.addPathDefinition("/logout","logout");
		return definition;
	}
}
addPathDefinition() 方法的第二个参数是拦截器链中的一个过滤器(Filter),它将应用于与指定路径匹配的所有请求。

登录:

public String login(String name,String passwd,boolean rememberMe){
	Subject subject = SecurityUtils.getSubject();
	AuthenticationToken token = new UsernamePasswordToken(name,passwd,rememberMe);
	try{
		//登录
		subject.login(token);
		//登出
		//subject.logout();
		return "登陆成功";
	}catch(AuthenticationException e){
		e.printStackTrace();
		return "登陆失败";
}

Realm认证策略

  • AtLeastOneSuccessfulStrategy 只要有一个Realm验证成功,就视为认证成功(默认
  • FirstSuccessfulStrategy 第一个Realm认证成功,就视为认证成功,后面的Realm忽略验证
  • AllSuccessfulStrategy 全部Realm认证成功,才视为成功

实现:
ShiroConfig类配置SecurityManager时,

ModularRealmAuthenticator modularRealmAuthenticator = new ModularRealmAuthenticator();
//设置认证策略 AllSuccessfulStrategy
modularRealmAuthenticator.setAuthenticationStrategy(new AllSuccessfulStrategy());
dwsm.setAuthenticator(modularRealmAuthenticator);
//封装myRealm集合
List<Realm> list = new ArrayList<>();
list.add(myRealm);
list.add(myRealm2);
//将myRealm存入defaultWebSecurityManager
dwsm.setRealms(list);
return dwsm;

remember me

配置类ShiroConfig中配置

//配置defaultWebSecurityManager中添加
dwsm.setRememberMeManager(rememberMeManager());

//cookie 属性设置
public SimleCookie rememberMeCookie(){
	SimleCookie cookie = new SimpleCookie("rememberMe");
	//设置跨域
	//cookie.setDomain(domain);
	cookie.setPath("/");
	cookie.setHttpOnly(true);
	cookie.setMaxAge(30*24*60*60);
	return cookie;
}
//创建Shiro的cookie管理对象
public CookieRememberMeManager rememberMeManager(){
	CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
	cookieRememberMeManager.setCookie(rememberMeCookie());
	cookieRememberMeManager.setCipherKey("RememberMe的16位的Key的Cookie数据加密密钥".getBytes());
	return cookieRememberMeManager;
}

登出

在配置类ShiroConfig中的过滤器配置方法shiroFilterChainDefinition()中添加配置

//登出
definition.addPathDefinition("/logout","logout");

授权

自定义授权方法

获取当前登录用户的角色、权限信息,返回给shiro用来进行授权认证

  • myRealm继承AuthorizingRealm并重写doGetAuthorizationInfo(PrincipalCollection principalCollection)方法
  • 创建对象,封装当前登录用户的角色、权限信息
    • SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
  • 获取当前用户的身份信息
    • String principal = principalCollection.getPrimaryPrincipal().toString();
  • 调用业务层从数据库查询用户的角色信息
    • List roles = userService.getUserRoleInfo(principal);
  • 调用业务层从数据库查询用户的角色信息
    • List permissions= userService.getUserPermissionInfo(roles);
  • 存储角色
    • info.addRoles(roles);
  • 存储权限
    • info.addStringPermissions(permissions);
  • 返回信息
    • return info;
public class MyRealm extends AuthorizingRealm{
	@Autowired
	private UserService userService;
	//自定义授权方法
	@override
	public SimpleAuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection){
			//创建对象,封装当前登录用户的角色、权限信息
		 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		 //获取当前用户的身份信息
		 String principal = principalCollection.getPrimaryPrincipal().toString();
		 List<String> roles = userService.getUserRoleInfo(principal);
		 List<String> permissions= userService.getUserPermissionInfo(roles);
		 info.addRoles(roles);
		 info.addStringPermissions(permissions);
		 return info;
		 }
	}
}

默认授权注解@RequiresXXX()

  • @RequiresAuthentication
    • 验证用户是否登录,等同于方法subject.isAuthenticated()
  • @RequiresUser
    • 验证用户是否被记忆:
      • 登录认证成功subject.isAuthenticated()为true
      • 登录后被记忆subject.isRemembered()为true
  • @RequiresGuest
    • 验证是否是一个guest请求,是否是游客的请求
    • 此时subject.getPrincipal()为null
  • @RequiresRoles
    • 验证subject是否有相应角色,有角色访问方法,没有则会抛出异常AuthorizationException。
    • 例如:@RequiresRoles(“aRoleName”)
      • void someMethod();
    • 只有subject有aRoleName角色才能访问someMethod()方法
  • @RequiresPermissions
    • 验证subject是否有相应权限,有权限方法,没有则会抛出异常AuthorizationException。
    • 例如:@RequiresPermissions(“file:read”,“write:aFile.txt”)
      • void someMethod();
    • subject必须同时含有file:read 和 write:aFile.txt权限才能访问方法someMethod();

注解可以添加在类、方法、字段上,一般在Controller方法上

启用 Shiro 注解支持

  • 在 Spring Boot 主类中,添加 @EnableAspectJAutoProxy 和 @EnableWebMvc 注解,并添加一个名为 “org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor” 的 Bean,以启用 Shiro 注解的 AOP 支持。
@SpringBootApplication
@EnableAspectJAutoProxy
@EnableWebMvc
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}

异常处理

  • UnauthorizedException 无权限
  • AuthorizationException 权限认证失败
@ControllerAdvice
public class PermissionsException{
	@ResponseBody
	@ExceptionHandler(UnauthorizedException.class)
	public String unauthorizedException(Exception ex){
		return "无权限";
	}
	@ResponseBody
	@ExceptionHandler(AuthorizationException.class)
	public String authorizationException(Exception ex){
		return "权限认证失败";
	}
}
文章来源:https://blog.csdn.net/lx5210521/article/details/135326403
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。