shiro
一、shiro是什么?
Shiro是Apache下的一个开源项目。shiro属于轻量级框架,相对于SpringSecurity简单的多,也没有SpringSecurity那么复杂 。
二、主要功能
shiro主要有三大功能模块:
1. Subject:主体,一般指用户。
2. SecurityManager:安全管理器,管理所有Subject,可以配合内部安全组件。(类似于SpringMVC中的DispatcherServlet)
3. Realms:用于进行权限信息的验证,一般需要自己实现。
细分功能
1. Authentication:身份认证/登录(账号密码验证)。
2. Authorization:授权,即角色或者权限验证。
3. Session Manager:会话管理,用户登录后的session相关管理。
4. Cryptography:加密,密码加密等。
5. Web Support:Web支持,集成Web环境。
6. Caching:缓存,用户信息、角色、权限等缓存到如redis等缓存中。
7. Concurrency:多线程并发验证,在一个线程中开启另一个线程,可以把权限自动传播过去。
8. Testing:测试支持;
9. Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问。
10. Remember Me:记住我,登录后,下次再来的话不用登录了
三、具体实现
1、项目目录
pom.xml依赖文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId> <artifactId>shiro_demo</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.2.RELEASE</version> <relativePath/> </parent>
<properties> <java.version>1.8</java.version> <spring.shiro.version>1.6.0</spring.shiro.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${spring.shiro.version}</version> </dependency>
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
</project>
|
Permissions权限实体类
1 2 3 4 5 6 7 8 9 10 11
| package org.example.bean;
import lombok.AllArgsConstructor; import lombok.Data;
@Data @AllArgsConstructor public class Permissions { private String id; private String permissionsName; }
|
Role角色实体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package org.example.bean;
import lombok.AllArgsConstructor; import lombok.Data;
import java.util.Set;
@Data @AllArgsConstructor public class Role {
private String id; private String roleName;
private Set<Permissions> permissions; }
|
User实体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package org.example.bean;
import lombok.AllArgsConstructor; import lombok.Data;
import java.util.Set;
@Data @AllArgsConstructor public class User {
private String id; private String userName; private String password;
private Set<Role> roles; }
|
业务层,模拟数据库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| package org.example.service.impl;
import lombok.extern.slf4j.Slf4j; import org.example.bean.Permissions; import org.example.bean.Role; import org.example.bean.User; import org.example.service.LoginService; import org.springframework.stereotype.Service;
import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set;
@Service @Slf4j public class LoginServiceImpl implements LoginService { @Override public User getUserByName(String getMapByName) { return getMapByName(getMapByName); }
private User getMapByName(String userName) { Permissions permissions1 = new Permissions("1", "query"); Permissions permissions2 = new Permissions("2", "add"); Set<Permissions> permissionsSet = new HashSet<>(2); permissionsSet.add(permissions1); permissionsSet.add(permissions2); Role role = new Role("1", "admin", permissionsSet); Set<Role> roleSet = new HashSet<>(); roleSet.add(role); User user = new User("1", "wsl", "123456", roleSet); Map<String, User> map = new HashMap<>(1); map.put(user.getUserName(), user);
Set<Permissions> permissionsSet1 = new HashSet<>(2); permissionsSet1.add(permissions1); Role role1 = new Role("2", "user", permissionsSet1); Set<Role> roleSet1 = new HashSet<>(); roleSet1.add(role1); User user1 = new User("2", "zhangsan", "123456", roleSet1); map.put(user1.getUserName(), user1); return map.get(userName); } }
|
CustomRealm用户权限配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| package org.example.shiro;
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.example.bean.User; import org.example.service.LoginService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.StringUtils;
public class CustomRealm extends AuthorizingRealm { @Autowired private LoginService loginService;
@Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { String userName = principalCollection.getPrimaryPrincipal().toString(); User user = loginService.getUserByName(userName);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); user.getRoles().forEach(role -> { simpleAuthorizationInfo.addRole(role.getRoleName()); role.getPermissions().forEach(permission -> simpleAuthorizationInfo.addStringPermission(permission.getPermissionsName())); }); return simpleAuthorizationInfo;
}
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { Object username = authenticationToken.getPrincipal(); if (StringUtils.isEmpty(username)) { return null; } String name = username.toString(); User user = loginService.getUserByName(name); if (user == null) { return null; } else { return new SimpleAuthenticationInfo(name, user.getPassword(), getName()); } } }
|
shiro配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| package org.example.config;
import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.example.shiro.CustomRealm; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
import java.util.HashMap; import java.util.Map;
@Configuration public class ShiroConfig {
@Bean @ConditionalOnMissingBean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator(); defaultAAP.setProxyTargetClass(true); return defaultAAP; }
@Bean public CustomRealm myShiroRealm() { return new CustomRealm(); }
@Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myShiroRealm()); return securityManager; }
@Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String, String> map = new HashMap<>(); map.put("/logout", "logout"); map.put("/**", "authc"); shiroFilterFactoryBean.setLoginUrl("/login"); shiroFilterFactoryBean.setSuccessUrl("/index"); shiroFilterFactoryBean.setUnauthorizedUrl("/error"); shiroFilterFactoryBean.setFilterChainDefinitionMap(map); return shiroFilterFactoryBean; }
@Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } }
|
ExceptionHandler统一异常处理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package org.example.handler;
import lombok.extern.slf4j.Slf4j; import org.apache.shiro.authz.AuthorizationException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice @Slf4j public class MyExceptionHandler {
@ExceptionHandler @ResponseBody public String ErrorHandler(AuthorizationException e) { log.error("没有通过权限验证!", e); return "没有通过权限验证!"; } }
|
controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| package org.example.controller;
import lombok.extern.slf4j.Slf4j; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authz.AuthorizationException; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.authz.annotation.RequiresRoles; import org.apache.shiro.subject.Subject; import org.example.bean.User; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;
@RestController @Slf4j public class UserController {
@GetMapping("/login") public String login(User user) { if (StringUtils.isEmpty(user.getUserName()) || StringUtils.isEmpty(user.getPassword())) { return "请输入用户名和密码!"; } Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken( user.getUserName(), user.getPassword() ); try { subject.login(usernamePasswordToken); } catch (UnknownAccountException e) { log.error("用户名不存在!", e); return "用户名不存在!"; } catch (AuthenticationException e) { log.error("账号或密码错误!", e); return "账号或密码错误!"; } catch (AuthorizationException e) { log.error("没有权限!", e); return "没有权限"; } return "login success"; }
@RequiresRoles("admin") @GetMapping("/admin") public String admin() { return "admin success!"; }
@RequiresPermissions("query") @GetMapping("/index") public String index() { return "index success!"; }
@RequiresPermissions("add") @GetMapping("/add") public String add() { return "add success!"; } }
|