流程图
1.主要功能介绍
Subject:主体,一般指用户。
SecurityManager:Shiro的核心部分,它负责安全认证与授权。Shiro本身已经实现了所有的细节,用户可以完全把它当做一个黑盒来使用,本质上就是一个工厂类似Spring中的ApplicationContext安全管理器,管理所有Subject,可以配合内部安全组件。(类似于SpringMVC中的DispatcherServlet)
Realms:用于进行权限信息的验证,一般需要自己实现,Shiro需要根据用户名和密码首先判断登录的用户是否合法,然后再对合法用户授权。而这个过程就是Realm的实现过程。
2 详细功能
Authentication:身份认证/登录(账号密码验证)。
Authorization:授权,即角色或者权限验证。
Session Manager:会话管理,用户登录后的session相关管理。
Cryptography:加密,密码加密等。
Web Support:Web支持,集成Web环境。
Caching:缓存,用户信息、角色、权限等缓存到如redis等缓存中。
Concurrency:多线程并发验证,在一个线程中开启另一个线程,可以把权限自动传播过去。
Testing:测试支持;
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问。
Remember Me:记住我,登录后,下次再来的话不用登录了
2.具体实现
2.1 引入依赖
2.2 模拟数据库信息
/** * 模拟数据库查询 * * @param userName 用户名 * @return User */ private User getMapByName(String userName) { Permissions permissions1 = new Permissions("1", "query"); Permissions permissions2 = new Permissions("2", "add"); Set<Permissions> permissionsSet = new HashSet<>(); 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<>(); map.put(user.getUserName(), user); Set<Permissions> permissionsSet1 = new HashSet<>(); 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); }
2.3 自定义Realm用于查询用户的角色和权限信息并保存到权限管理器
public class CustomRealm extends AuthorizingRealm { @Autowired private LoginService loginService; /** * @MethodName doGetAuthorizationInfo * @Description 权限配置类 * @Param [principalCollection] * @Return AuthorizationInfo * @Author WangShiLin */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //获取登录用户名 String name = (String) principalCollection.getPrimaryPrincipal(); //查询用户名称 User user = loginService.getUserByName(name); //添加角色和权限 SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); for (Role role : user.getRoles()) { //添加角色 simpleAuthorizationInfo.addRole(role.getRoleName()); //添加权限 for (Permissions permissions : role.getPermissions()) { simpleAuthorizationInfo.addStringPermission(permissions.getPermissionsName()); } } return simpleAuthorizationInfo; } /** * @MethodName doGetAuthenticationInfo * @Description 认证配置类 * @Param [authenticationToken] * @Return AuthenticationInfo * @Author WangShiLin */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { if (StringUtils.isEmpty(authenticationToken.getPrincipal())) { return null; } //获取用户信息 String name = authenticationToken.getPrincipal().toString(); User user = loginService.getUserByName(name); if (user == null) { //这里返回后会报出对应异常 return null; } else { //这里验证authenticationToken和simpleAuthenticationInfo的信息 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, user.getPassword().toString(), getName()); return simpleAuthenticationInfo; } } }
2.4 构建shiro配置类 ShiroConfig.java,把CustomRealm和SecurityManager等注入到spring容器中(主要用来加入自己的相关验证方式 对跳转路径或根据其权限进行相应的过滤)
@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(); } //权限管理,配置主要是Realm的管理认证 @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myShiroRealm()); return securityManager; } //Filter工厂,设置对应的过滤条件和跳转条件 @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; } }
2.5 创建LoginController 对相应权限信息用户进行验证
@RestController @Slf4j public class LoginController { @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); subject.checkRole("admin"); subject.checkPermissions("query", "add"); } 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!"; } }
3.测试
首先先测试有相关权限的用户:
http://localhost:8080/login?userName=wsl&password=123456
最后测试无权限用户:
http://localhost:8080/login?userName=zhangsan&password=123456