一,Shiro 体系结构
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
- Authentication 认证 ---- 用户登录
- Authorization 授权 ---- 用户具有哪些权限
- Cryptography 安全数据加密
- Session Management 会话管理
- Web Integration web系统集成
- Interations 集成其它应用,spring、缓存框架
二,构建spring boot工程
建立Maven项目
修改pom.xml
- 继承Spring Boot 父工程
org.springframework.boot spring-boot-starter-parent 2.5.3
- 添加web支持
org.springframework.boot spring-boot-starter-web
编写spring Boot启动类
package com.example;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); }}
编写测试Controller类
package com.example.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;@Controllerpublic class TestController { @GetMapping("/hello") @ResponseBody public String hello(){ System.out.println("UserController.hello()"); return "ok"; }}
启动,测试
三,引入thymeleaf页面模块
修改pom.xml
- 添加thymeleaf依赖
org.springframework.boot spring-boot-starter-thymeleaf
在Controller添加测试方法
@RequestMapping("/test") public String testThymeleaf(Model model){ //把数据存入model model.addAttribute("name", "张三"); //返回test.html return "test"; }
建立test.html页面
- 在src/main/resource目录下创建templates目录,然后创建test.html页面
Title
启动,测试
四, Spring Boot与Shiro整合实现用户认证
核心API
- Subject: 用户主体(把操作交给SecurityManager)
- SecurityManager:安全管理器(关联Realm)
- Realm:Shiro连接数据的桥梁
修改pom.xml
- 添加shiro与spring整合依赖
org.apache.shiro shiro-spring 1.10.1
创建Realm类
package com.example.shiro;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.AuthenticationInfo;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.authz.AuthorizationInfo;import org.apache.shiro.realm.AuthorizingRealm;import org.apache.shiro.subject.PrincipalCollection;public class UserRleam extends AuthorizingRealm { @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("执行授权逻辑"); return null; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("执行认证逻辑"); return null; }}
编写Shiro配置类
package com.example.shiro;import org.apache.shiro.spring.web.ShiroFilterFactoryBean;import org.apache.shiro.web.mgt.DefaultWebSecurityManager;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import java.util.LinkedHashMap;import java.util.Map;@Configurationpublic class ShiroConfig { @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //设置安全管理器 shiroFilterFactoryBean.setSecurityManager(securityManager); //添加Shiro内置过滤器 Map filterMap = new LinkedHashMap(); filterMap.put("/hello", "anon"); filterMap.put("/login", "anon"); filterMap.put(" @Bean public DefaultWebSecurityManager getDefaultWebSecurityManager(UserRealm userRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //关联realm securityManager.setRealm(userRealm); return securityManager; } @Bean public UserRealm getRealm(){ return new UserRealm(); }}
创建登录页面
- 在src/main/resource目录下创建templates目录,然后创建login.html页面
登陆页面 登录
编写Controller的登录逻辑
- 在TestController.java类中添加方法
@GetMapping("/login") public String login(){ return "login"; } @PostMapping("/login") public String login(String username,String password,Model model){ //1.获取Subject Subject subject = SecurityUtils.getSubject(); //2.封装用户数据 UsernamePasswordToken token = new UsernamePasswordToken(username,password); //3.执行登录方法 try { subject.login(token); //登录成功 //跳转到test.html return "redirect:/test"; } catch (UnknownAccountException e) { //e.printStackTrace(); //登录失败:用户名不存在 System.out.println("用户名不存在"); return "login"; }catch (IncorrectCredentialsException e) { //e.printStackTrace(); //登录失败:密码错误 System.out.println("密码错误"); return "login"; } }
编写Realm的认证逻辑判断
package com.example.shiro;import org.apache.shiro.authc.*;import org.apache.shiro.authz.AuthorizationInfo;import org.apache.shiro.realm.AuthorizingRealm;import org.apache.shiro.subject.PrincipalCollection;public class UserRealm extends AuthorizingRealm { @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("执行授权逻辑"); return null; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("执行认证逻辑"); //假设数据库的用户名和密码 String username = "aaa"; String password = "123"; //编写shiro判断逻辑,判断用户名和密码 //1.判断用户名 UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken; if(!token.getUsername().equals(username)){ //用户名不存在 return null;//shiro底层会抛出UnKnowAccountException } //2.判断密码 return new SimpleAuthenticationInfo("flk好帅", password,null, getName()); }}
启动,测试
- 先访问localhost:8080/hello
由于在shiro过滤器中添加了filterMap.put(“/hello”, “anon”);,所以无需认证(登录)就可以访问/hello
- 访问localhost:8080/test
在src/main/resources/mapper目录下 添加thymeleaf与shiro的扩展 配置ShiroDialect,用于thymeleaf和shiro标签配合使用 来源地址:https://blog.csdn.net/fangliangke/article/details/128818634
由于在shiro过滤器中添加了filterMap.put(“*Mapper.xml# 日志配置logging: level: com.example: debug 创建数据库,并创建表
添加User.java
package com.example.domain;public class User { private Integer id; private String username; private String password; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; }}
添加UserMapper.java
package com.example.mapper;import com.example.domain.User;import org.apache.ibatis.annotations.Param;import org.springframework.stereotype.Repository;public interface UserMapper { User findByUsername(@Param("username") String username);}
添加UserMapper.xml
添加Service层
package com.example.service;import com.example.domain.User;public interface IUserService { User findByUsername (String username);}
package com.example.service.impl;import com.example.domain.User;import com.example.mapper.UserMapper;import com.example.service.IUserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Servicepublic class UserServiceImpl implements IUserService { @Autowired private UserMapper userMapper; @Override public User findByUsername(String username) { return userMapper.findByUsername(username); }}
在启动类Application.java中添加@MapperScan注解
package com.example;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication//@MapperScan指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类@MapperScan("com.example.mapper")public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); }}
修改UserRealm中doGetAuthenticationInfo方法的认证逻辑
package com.example.shiro;import com.example.domain.User;import com.example.service.IUserService;import org.apache.shiro.authc.*;import org.apache.shiro.authz.AuthorizationInfo;import org.apache.shiro.realm.AuthorizingRealm;import org.apache.shiro.subject.PrincipalCollection;import org.springframework.beans.factory.annotation.Autowired;public class UserRealm extends AuthorizingRealm { @Autowired private IUserService userService; @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("执行授权逻辑"); return null; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("执行认证逻辑"); //假设数据库的用户名和密码// String username = "aaa";// String password = "123"; //编写shiro判断逻辑,判断用户名和密码 //1.判断用户名 UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken; //从数据库中获取用户信息 User user = userService.findByUsername(token.getUsername()); if(user == null){ //用户名不存在 return null;//shiro底层会抛出UnKnowAccountException } //2.判断密码 return new SimpleAuthenticationInfo("flk好帅", user.getPassword(),null, getName()); }}
启动,测试
六,Spring Boot与Shiro整合实现用户授权
修改pom.xml
修改ShiroConfig.java
@Bean public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor( DefaultWebSecurityManager defaultWebSecurityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager); return authorizationAttributeSourceAdvisor; }
完善UserRealm的doGetAuthorizationInfo授权逻辑
@Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("执行授权逻辑"); //给资源进行授权 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //假设数据库的aaa用户的权限只有user:add info.addStringPermission("user:add"); //info.addStringPermission("user:update"); return info; }
添加两个页面
addPage
updatePage
修改test.html页面
修改TestController.java添加页面跳转方法
//需要user:add权限才能访问 @RequiresPermissions("user:add") @GetMapping("https://blog.csdn.net/add") public String add(){ return "add"; } //需要user:update权限才能访问 @RequiresPermissions("user:update") @GetMapping("https://blog.csdn.net/update") public String update(){ return "update"; }
启动,测试
成功进入,因为它有user:add这个权限。
– 控制台打印:
失败进入,因为它没有user:update这个权限。
– 控制台打印:
(后续可以做全局异常捕获跳转到提示页面,我这边没有去弄。。。)七,thymeleaf和shiro标签整合使用
修改pom.xml
修改ShiroConfig.java
@Bean public ShiroDialect getShiroDialect(){ return new ShiroDialect(); }
修改test.html页面
启动,测试
控制台打印: