安全无处不在,趁着放假读了一下 Shiro 文档,并记录一下 Shiro 整合 Spring Boot 在数据库中根据角色控制访问权限html
Apache Shiro是一个功能强大、灵活的,开源的安全框架。它能够干净利落地处理身份验证、受权、企业会话管理和加密。java
上图是 Shiro 的基本架构mysql
有时被称为“登陆”,用来证实用户是用户他们本身本人算法
访问控制的过程,即肯定“谁”访问“什么”spring
管理用户特定的会话,在 Shiro 里面能够发现全部的用户的会话信息都会由 Shiro 来进行控制sql
在对数据源使用加密算法加密的同时,保证易于使用数据库
Spring Boot 1.5.9
MySQL 5.7
Maven 3.5.2
Spring Data Jpa
Lombokapache
这里只给出主要的 Shiro 依赖安全
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.4.0-RC2</version> </dependency>
咱们暂时只须要用户表、角色表,在 Spring boot 中修改配置文件将自动为咱们建立数据库表架构
server: port: 8888 spring: datasource: driver-class-name: com.mysql.jdbc.Driver username: root password: root url: jdbc:mysql://localhost:3306/shiro?characterEncoding=utf-8&useSSL=false jpa: generate-ddl: true hibernate: ddl-auto: update show-sql: true
@Data @Entity public class Role { @Id @GeneratedValue private Integer id; private Long userId; private String role; }
@Data @Entity public class User { @Id @GeneratedValue private Long id; private String username; private String password; }
首先创建 Realm 类,继承自 AuthorizingRealm,自定义咱们本身的受权和认证的方法。Realm 是能够访问特定于应用程序的安全性数据(如用户,角色和权限)的组件。
public class Realm extends AuthorizingRealm { @Autowired private UserService userService; //受权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //从凭证中得到用户名 String username = (String) SecurityUtils.getSubject().getPrincipal(); //根据用户名查询用户对象 User user = userService.getUserByUserName(username); //查询用户拥有的角色 List<Role> list = roleService.findByUserId(user.getId()); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); for (Role role : list) { //赋予用户角色 info.addStringPermission(role.getRole()); } return info; } //认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //得到当前用户的用户名 String username = (String) authenticationToken.getPrincipal(); //从数据库中根据用户名查找用户 User user = userService.getUserByUserName(username); if (userService.getUserByUserName(username) == null) { throw new UnknownAccountException( "没有在本系统中找到对应的用户信息。"); } SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(),getName()); return info; } }
@Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); //如下是过滤链,按顺序过滤,因此/**须要放最后 //开放的静态资源 filterChainDefinitionMap.put("/favicon.ico", "anon");//网站图标 filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(myRealm()); return defaultWebSecurityManager; } @Bean public MyRealm myRealm() { MyRealm myRealm = new MyRealm(); return myRealm; } }
@Controller public class UserController { @Autowired private UserService userService; @GetMapping("/") public String index() { return "index"; } @GetMapping("/login") public String toLogin() { return "login"; } @GetMapping("/admin") public String admin() { return "admin"; } @PostMapping("/login") public String doLogin(String username, String password) { UsernamePasswordToken token = new UsernamePasswordToken(username, password); Subject subject = SecurityUtils.getSubject(); try { subject.login(token); } catch (Exception e) { e.printStackTrace(); } return "redirect:admin"; } @GetMapping("/home") public String home() { Subject subject = SecurityUtils.getSubject(); try { subject.checkPermission("admin"); } catch (UnauthorizedException exception) { System.out.println("没有足够的权限"); } return "home"; } @GetMapping("/logout") public String logout() { return "index"; } }
@Service public class UserService { @Autowired private UserDao userDao; public User getUserByUserName(String username) { return userDao.findByUsername(username); } @RequiresRoles("admin") public void send() { System.out.println("我如今拥有角色admin,能够执行本条语句"); } }
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <html lang="en"/> <head> <meta charset="UTF-8"/> <title>Title</title> </head> <body> <form action="/login" method="post"> <input type="text" name="username" /> <input type="password" name="password" /> <input type="submit" value="登陆" /> </form> </body> </html>
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <html lang="en"/> <head> <meta charset="UTF-8"/> <title>Title</title> </head> <body> home </body> </html>
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <html lang="en"/> <head> <meta charset="UTF-8"/> <title>Title</title> </head> <body> index <a href="/login">请登陆</a> </body> </html>
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <html lang="en"/> <head> <meta charset="UTF-8"/> <title>Title</title> </head> <body> <form action="/login" method="post"> <input type="text" name="username" /> <input type="password" name="password" /> <input type="submit" value="登陆" /> </form> </body> </html>
这个小案例实现了根据角色来控制用户访问,其中最重要的就是 Realm,它充当了Shiro与应用安全数据间的“桥梁”或者“链接器”