前言
大家好,一直以来我都本着用最通俗的话理解核心的知识点, 我认为所有的难点都离不开 基础知识 的铺垫。目前正在出一个SpringBoot
长期系列教程,从入门到进阶, 篇幅会较多~
适合人群
- 学完Java基础
- 想通过Java快速构建web应用程序
- 想学习或了解SpringBoot
- SpringBoot进阶学习
大佬可以绕过 ~
背景
如果你是一路看过来的,很高兴你能够耐心看完。之前带大家学了Springboot
基础部分,对基本的使用有了初步的认识, 接下来的几期内容将会带大家进阶使用,会先讲解基础中间件
的使用和一些场景的应用,或许这些技术你听说过,没看过也没关系,我会带大家一步一步的入门,耐心看完你一定会有收获
~
情景回顾
上期带大家学习了Shiro安全框架
的基本概念和工作原理, 相信有了基本的认识之后,下面学习将会容易些,本期将带大家学习整合Shiro框架
和基本使用。同样的,我们集成到Springboot
中。
项目源码(持续更新 欢迎star⭐️)
环境搭建
我们先修改pom.xml
, 引入相关依赖, 为了后期方便学习,我对项目结构做了一些调整,在目录下新建一个模块用于Shiro
学习, 具体大家可以查看源码:
<!-- shiro-spring --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> 复制代码
配置 Shiro
今天带大家实现一个用户认证的功能, 我们先添加配置ShiroConfig
:
@Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 设置securityManager shiroFilterFactoryBean.setSecurityManager((SecurityManager) securityManager); // 登录的url shiroFilterFactoryBean.setLoginUrl("/login"); // 登录成功后跳转的url shiroFilterFactoryBean.setSuccessUrl("/index"); // 未授权url shiroFilterFactoryBean.setUnauthorizedUrl("/403"); LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); // 定义filterChain,静态资源不拦截 filterChainDefinitionMap.put("/css/**", "anon"); filterChainDefinitionMap.put("/js/**", "anon"); filterChainDefinitionMap.put("/fonts/**", "anon"); filterChainDefinitionMap.put("/img/**", "anon"); // druid数据源监控页面不拦截 filterChainDefinitionMap.put("/druid/**", "anon"); // 配置退出过滤器,其中具体的退出代码Shiro已经替我们实现了 filterChainDefinitionMap.put("/logout", "logout"); filterChainDefinitionMap.put("/", "anon"); // 除上以外所有url都必须认证通过才可以访问,未通过认证自动访问LoginUrl filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public DefaultWebSecurityManager securityManager(){ // 配置SecurityManager,并注入shiroRealm DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(shiroRealm()); return securityManager; } /** * 自定义实现的Realm * @return */ @Bean public CustomRealm shiroRealm(){ CustomRealm shiroRealm = new CustomRealm(); return shiroRealm; } } 复制代码
简单说下, anon
代表的是允许资源公开访问, authc
代表的是资源需要通过认证后才能访问。其实大部分都已经贴在注释上了,大家跟着配就好,重点关注的是shiroRealm()
这个方法, 里边有一个类需要我们自行实现,下边我就带大家实现一下:
public class CustomRealm extends AuthorizingRealm { /** * 获取用户角色和权限 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) { return null; } /** * 登录认证 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // 获取用户输入的用户名和密码 String userName = (String) token.getPrincipal(); String password = new String((char[]) token.getCredentials()); System.out.println("用户" + userName + "认证-----ShiroRealm.doGetAuthenticationInfo"); // 通过用户名到数据库查询用户信息 User user = UserMock.getUserByUsername(userName); if (user == null) { throw new UnknownAccountException("用户名或密码错误!"); } if (!password.equals(user.getPassword())) { throw new IncorrectCredentialsException("用户名或密码错误!"); } if (user.getState().equals("0")) { throw new LockedAccountException("账号已被锁定,请联系管理员!"); } SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName()); return info; } } 复制代码
这里为了方便演示,我直接写死到对象里,使用测试数据来模拟数据库查询到的用户信息,真实业务中是要去数据库查询用户信息的:
@Data public class User { public Integer id; public String username; public String password; public String role; public String state; } 复制代码
定义一个 UserMock
类
public class UserMock { public static String getPassword(String username) { System.out.println("用户名: " + username); if(username.equals("admin")) { return "123"; }else { return "456"; } } public static String getRole(String username) { if(username.equals("admin")) { return "admin"; }else { return "user"; } } public static User getUserByUsername(String username) { User user = new User(); if(username.equals("admin")) { user.setUsername("admin"); user.setPassword("123"); user.setRole("admin"); }else { user.setUsername("admin1"); user.setPassword("456"); user.setRole("user"); } user.setState("1"); return user; } } 复制代码
定义一个首页控制器:
@RestController public class IndexController { @RequestMapping("/index") public String index(Model model) { // 登录成后,即可通过Subject获取登录的用户信息 User user = (User) SecurityUtils.getSubject().getPrincipal(); model.addAttribute("user", user); return "index"; } } 复制代码
定义一个登录控制器:
@RestController public class LoginController { @GetMapping("/login") public String login() { return "login"; } @PostMapping("/login") @ResponseBody public String login(String username, String password) { UsernamePasswordToken token = new UsernamePasswordToken(username, password); // 获取Subject对象 Subject subject = SecurityUtils.getSubject(); try { // 执行登录 subject.login(token); return "ok"; } catch (UnknownAccountException e) { return e.getMessage(); } catch (IncorrectCredentialsException e) { return "IncorrectCredentialsException " + e.getMessage(); } catch (LockedAccountException e) { return "LockedAccountException " + e.getMessage(); } catch (AuthenticationException e) { return "认证失败!"; } } } 复制代码
运行项目,访问: http://localhost:8878/api/index
, 发现没法直接访问自动跳转到了http://localhost:8878/api/login;jsessionid=F7E2EABECB7D87B89EA99C328B344B05
,说明我们还未登录, 下面进行登录 POST http://localhost:8878/api/login
, 登录成功后会自动跳转/index
, 并且返回结果 index --->admin
, 说明我们之前的配置都生效了,并且可以通过Subject
拿到用户信息
结束语
本期内容就到这里结束了, 总结一下主要讲了在SpringBoot
中如何集成Shiro
,以及带大家学习了它的基本配置,最后我们完成了一个用户认证的功能。
下期预告
细心的同学可能会发现,当我们浏览器关闭在进来访问首页index
的时候,发现又跳转回了登录,这样的交互肯定是不友好的,所以下期带大家学习如何在Shiro
中存储我们的Cookie
,让它记住我们的信息