Spring Boot 整合Shiro(一)登录认证和授权(附源码)

                                           

shiro

 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。

主要功能

三个核心组件:Subject, SecurityManager 和 Realms.

Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。
  Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
  SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
  Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
  从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。
  Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。

 

1.项目版本


 
 
  1. Spring Boot 2 .x
  2. shiro 1 .3 .2

1.1导入依赖


 
 
  1.         <dependency>
  2.             <groupId>org.apache.shiro </groupId>
  3.             <artifactId>shiro-spring </artifactId>
  4.             <version>1.3.2 </version>
  5.         </dependency>

2.类配置

2.1 ShiroConfig

相当于之前的xml配置,包括:过滤的文件和权限,密码加密的算法,其用注解等相关功能。


 
 
  1. import com.shiro.realm.CustomRealm;
  2. import org.apache.shiro.mgt.SecurityManager;
  3. import org.apache.shiro.spring.LifecycleBeanPostProcessor;
  4. import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
  5. import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
  6. import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
  7. import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
  8. import org.springframework.context.annotation.Bean;
  9. import org.springframework.context.annotation.Configuration;
  10. import org.springframework.context.annotation.DependsOn;
  11. import java.util.LinkedHashMap;
  12. import java.util.Map;
  13. /**
  14. * 过滤的文件和权限,密码加密的算法,其用注解等相关功能
  15. */
  16. @Configuration
  17. public class ShiroConfig {
  18.     @Bean(name = "shiroFilter")
  19.     public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
  20.        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
  21.         // Shiro的核心安全接口,这个属性是必须的
  22.        shiroFilterFactoryBean.setSecurityManager(securityManager);
  23.         // 身份认证失败,则跳转到登录页面的配置
  24.        shiroFilterFactoryBean.setLoginUrl( "/login");
  25.         // 权限认证失败,则跳转到指定页面
  26.        shiroFilterFactoryBean.setUnauthorizedUrl( "/notRole");
  27.        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
  28.         // <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
  29.        filterChainDefinitionMap.put( "/webjars/**", "anon");
  30.        filterChainDefinitionMap.put( "/login", "anon");
  31.        filterChainDefinitionMap.put( "/", "anon");
  32.        filterChainDefinitionMap.put( "/front/**", "anon");
  33.        filterChainDefinitionMap.put( "/api/**", "anon");
  34.        filterChainDefinitionMap.put( "/admin/**", "authc");
  35.        filterChainDefinitionMap.put( "/user/**", "authc");
  36.         //主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截 剩余的都需要认证
  37.        filterChainDefinitionMap.put( "/**", "authc");
  38.        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
  39.         return shiroFilterFactoryBean;
  40.    }
  41.     @Bean
  42.     public SecurityManager securityManager() {
  43.        DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
  44.        defaultSecurityManager.setRealm(customRealm());
  45.         return defaultSecurityManager;
  46.    }
  47.     @Bean
  48.     public CustomRealm customRealm() {
  49.        CustomRealm customRealm = new CustomRealm();
  50.         return customRealm;
  51.    }
  52. }

2.1.1 shiroFilter方法

shiro的过滤器,可以设置登录页面(setLoginUrl)、权限不足跳转页面(setUnauthorizedUrl)、具体某些页面的权限控制或者身份认证。
注意:这里是需要设置SecurityManager(setSecurityManager)。
默认的过滤器还有:anno、authc、authcBasic、logout、noSessionCreation、perms、port、rest、roles、ssl、user过滤器。
具体的大家可以查看package org.apache.shiro.web.filter.mgt.DefaultFilter。这个类,常用的也就authc、anno。

2.1.2 securityManager 方法


 
 
  1. public interface SecurityManager extends Authenticator, Authorizer, SessionManager {
  2.     //登录方法
  3.     Subject login(Subject subject, AuthenticationToken authenticationToken) throws AuthenticationException;
  4.     //注销方法
  5.     void logout(Subject subject);
  6.     //创建subject
  7.       Subject createSubject(SubjectContext context);
  8. }

由于项目是一个web项目,所以我们使用的是DefaultWebSecurityManager ,然后设置自己的Realm。

2.1.3 CustomRealm 方法

将 customRealm的实例化交给spring去管理,当然这里也可以利用注解的方式去注入

 

2.2 CustomRealm

自定义的CustomRealm继承AuthorizingRealm

并且重写父类中的doGetAuthorizationInfo(权限相关)、doGetAuthenticationInfo(身份认证)这两个方法。


 
 
  1. import org.apache.shiro.SecurityUtils;
  2. import org.apache.shiro.authc.*;
  3. import org.apache.shiro.authz.AuthorizationInfo;
  4. import org.apache.shiro.authz.SimpleAuthorizationInfo;
  5. import org.apache.shiro.realm.AuthorizingRealm;
  6. import org.apache.shiro.subject.PrincipalCollection;
  7. import java.util.HashSet;
  8. import java.util.Set;
  9. /**
  10. *
  11. */
  12. public class CustomRealm extends AuthorizingRealm {
  13.     /**
  14.     * 权限相关
  15.     * @param principalCollection
  16.     * @return
  17.     */
  18.     @Override
  19.     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
  20.         //账户
  21.        String username = (String) SecurityUtils.getSubject().getPrincipal();
  22.        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
  23.         //从数据库获取账户权限信息
  24.        Set<String> stringSet = new HashSet<>();
  25.        stringSet.add( "user:show");
  26.        stringSet.add( "user:admin");
  27.        info.setStringPermissions(stringSet);
  28.         return info;
  29.    }
  30.     /**
  31.     * 身份认证
  32.     * 这里可以注入userService,为了方便演示直接写死账户和密码
  33.     * 获取即将需要认证的信息
  34.     * @param authenticationToken
  35.     * @return
  36.     * @throws AuthenticationException
  37.     */
  38.     @Override
  39.     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
  40.        System.out.println( "-------身份认证方法--------");
  41.        String userName = (String) authenticationToken.getPrincipal();
  42.        String userPwd = new String(( char[]) authenticationToken.getCredentials());
  43.        System.out.println(userPwd);
  44.         //根据用户名从数据库获取密码
  45.        String password = "123";
  46.         if (userName == null) {
  47.             throw new AccountException( "用户名不正确");
  48.        } else if (!userPwd.equals(password )) {
  49.             throw new AccountException( "密码不正确");
  50.        }
  51.         return new SimpleAuthenticationInfo(userName, password,getName());
  52.    }
  53. }

自定义的Realm类继承AuthorizingRealm类,并且重载doGetAuthorizationInfodoGetAuthenticationInfo两个方法。
doGetAuthorizationInfo: 权限认证,即登录过后,每个身份不一定,对应的所能看的页面也不一样。
doGetAuthenticationInfo:身份认证。即登录通过账号和密码验证登陆人的身份信息。
 

3.实战演练

3.1 登陆认证

创建LoginController


 
 
  1. import org.apache.shiro.SecurityUtils;
  2. import org.apache.shiro.authc.*;
  3. import org.apache.shiro.subject.Subject;
  4. import org.springframework.web.bind.annotation.*;
  5. @RestController
  6. @RequestMapping
  7. public class LoginController {
  8.     @RequestMapping(value = "/login", method = RequestMethod.GET)
  9.     public String defaultLogin() {
  10.         return "首页";
  11.    }
  12.     @RequestMapping(value = "/login", method = RequestMethod.POST)
  13.     public String login(@RequestParam("username") String username, @RequestParam("password") String password) {
  14.         // 从SecurityUtils里边创建一个 subject
  15.        Subject subject = SecurityUtils.getSubject();
  16.         // 在认证提交前准备 token(令牌)
  17.        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
  18.         // 执行认证登陆
  19.         try {
  20.            subject.login(token);
  21.        } catch (UnknownAccountException uae) {
  22.             return "未知账户";
  23.        } catch (IncorrectCredentialsException ice) {
  24.             return "密码不正确";
  25.        } catch (LockedAccountException lae) {
  26.             return "账户已锁定";
  27.        } catch (ExcessiveAttemptsException eae) {
  28.             return "用户名或密码错误次数过多";
  29.        } catch (AuthenticationException ae) {
  30.             return "用户名或密码不正确!";
  31.        }
  32.         if (subject.isAuthenticated()) {
  33.             return "登录成功";
  34.        } else {
  35.            token.clear();
  36.             return "登录失败";
  37.        }
  38.    }
  39. }

测试结果:

3.2 权限测试

在ShiroConfig添加如下代码,开启注解。


 
 
  1. @Bean
  2.     public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
  3.         return new LifecycleBeanPostProcessor();
  4.    }
  5.     /**
  6.     * *
  7.     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
  8.     * *
  9.     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
  10.     * * @return
  11.     */
  12.     @Bean
  13.     @DependsOn({ "lifecycleBeanPostProcessor"})
  14.     public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
  15.        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
  16.        advisorAutoProxyCreator.setProxyTargetClass( true);
  17.         return advisorAutoProxyCreator;
  18.    }
  19.     @Bean
  20.     public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
  21.        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
  22.        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
  23.         return authorizationAttributeSourceAdvisor;
  24.    }

创建UserController


 
 
  1. import com.shiro.utils.BaseController;
  2. import org.apache.shiro.authz.annotation.RequiresPermissions;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.bind.annotation.RestController;
  5. @RequestMapping( "/user")
  6. @RestController
  7. public class UserController extends BaseController {
  8.     @RequiresPermissions( "user:list")
  9.     @RequestMapping( "/show")
  10.     public String showUser() {
  11.         return "张三信息";
  12.    }
  13. }

再创建 BaseController,UserController继承BaseController,用于捕获没有权限时的异常


 
 
  1. import org.apache.shiro.authz.AuthorizationException;
  2. import org.apache.shiro.authz.UnauthorizedException;
  3. import org.springframework.web.bind.annotation.ExceptionHandler;
  4. import java.util.HashMap;
  5. import java.util.Map;
  6. public class BaseController {
  7.     /**
  8.     * 捕获没有权限时的异常
  9.     * @return
  10.     */
  11.     @ExceptionHandler({ UnauthorizedException.class, AuthorizationException.class })
  12.     public Map<String, Object> authorizationException(){
  13.        Map<String, Object> map = new HashMap<String, Object>();
  14.        System.out.println( "没有权限");
  15.        map.put( "success", true);
  16.        map.put( "msg", "当前用户没有此权限");
  17.         return map;
  18.    }
  19. }

运行测试,先执行 http://127.0.0.1:8080/login,再执行http://127.0.0.1:8080/user/show

方法上是 @RequiresPermissions("user:list"),之前在customRealm只添加了两个所以没有权限

现在把方法上的改为@RequiresPermissions("user:show")测试

现在就有权限访问数据啦

4.项目源码

项目地址

 

下篇介绍《Spring Boot 整合Shiro(二)加密登录与密码加盐处理》

 

 

                                   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值