ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
[TOC] ## 1. 基于url拦截-用户认证实现 ![](https://box.kancloud.cn/44c04c006c8288c57a15f718ed6c6d31_577x532.png) 蓝色为用户认证,绿色为权限管理 ### 1.1 用户认证 > * ShiroConfiguration.java:shiro启动时候的初始化工作,比如哪些是需要认证,哪些不需要认证;缓存配置设置;shiro权限数据在页面展示时整合需要的模板套件配置,等等。 > * ShiroRealm.java:shiro权限认证的具体实现代码,因为shiro本身只提供拦截路由,而具体如何数据源则由用户自己提供,不同的项目不同的要求,要不要加缓存登陆验证次数,要不要密码加密设置其他具体方式,这些都由用户自己决定,而shiro只提供给用户权限验证的格式接口,通过用户提供的数据源shrio判断要不要给具体用户授权请求路径的判断。 > * ShiroRealm 涉及到以下点: > * principal:主体,就是登陆的当前用户类型的数据实体 > * credentials:凭证,用户的密码,具体加密方式用户自己实现,什么都不做就是原文 > * Roles:用户拥有的角色标识(角色名称,admin,account,customer_service),字符串格式列表:用户拥有多个角色的可能 > * Permissions:用户拥有的权限标识(每个权限唯一标识,比如主键或者权限唯一标识编码),字符串格式列表:用户拥有多个权限的可能 **身份认证** > 在认证、授权内部实现机制中都有提到,最终处理都将交给Real进行处理。因为在Shiro中,最终是通过Realm来获取应用程序中的用户、角色及权限信息的。通常情况下,在Realm中会直接从我们的数据源中获取Shiro需要的验证信息。可以说,Realm是专用于安全框架的DAO. **认证实现** > Shiro的认证过程最终会交由Realm执行,这时会调用Realm的getAuthenticationInfo(token)方法。 > 该方法主要执行以下操作: > 1、检查提交的进行认证的令牌信息 > 2、根据令牌信息从数据源(通常为数据库)中获取用户信息 > 3、对用户信息进行匹配验证。 > 4、验证通过将返回一个封装了用户信息的AuthenticationInfo实例。 > 5、验证失败则抛出AuthenticationException异常信息。 而在我们的应用程序中要做的就是自定义一个Realm类,继承AuthorizingRealm抽象类,重载doGetAuthenticationInfo (),重写获取用户信息的方法。 在权限管理系统中,有这么几个角色很重要,这个要是不清楚的话,那么就很难理解,我们为什么这么编码了。 >用户表: >在用户表中保存了用户的基本信息,账号、密码、姓名,性别等; > 权限表: > (资源+控制权限)这个表中主要是保存了用户的URL地址,权限信息; > 角色表: > 在这个表重要保存了系统存在的角色; > 关联表: > 用户-角色管理表(用户在系统中都有什么角色,比如admin,vip等), > 角色-权限关联表(每个角色都有什么权限可以进行操作)。 ~~~ /** * Created by hanxt on 17-12-18. * 获取用户的角色和权限信息 */ @Component("aexitShrioRealm") public class AexitShrioRealm extends AuthorizingRealm { @Resource UserService userService; @Resource RoleService roleService; @Resource MenuService menuService; /** * 配置登录认证信息 * * @param authenticationToken * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; String userId = token.getUsername(); if (userId == null) { throw new AccountException("Null usernames are not allowed by this realm."); } //查出是否有此用户 SysUser curUser = userService.getByUserId(userId); if(curUser == null) throw new AccountException("account error:one user name must have one and only one user! "); //密码加密 String password = ShiroKit.md5(curUser.getPassword(),curUser.getSalt()); return new SimpleAuthenticationInfo(userId, password, getName()); } } ~~~ ### 1.2 权限控制 > 至于doGetAuthorizationInfo()是权限控制,当访问到页面的时候,使用了相应的注解或者shiro标签才会执行此方法否则不会执行,所以如果只是简单的身份认证没有权限的控制的话,那么这个方法可以不进行实现,直接返回null即可。 > 向shiro返回用户及用户拥有权限信息SimpleAuthorizationInfo,在使用shiro注解或者标签后,调用至于doGetAuthorizationInfo获取权限信息,并且对于用户要访问的URL与这个用户的权限匹配 #### 权限控制流程 1. 注解或者标签触发权限验证 2. doGetAuthorizationInfo()获取当前用户的角色和权限 3. 用数据库中的权限与访问的URL对比,有此权限访问通过,没有拒绝 我们在进行权限验证的时候,我们只需要重写oGetAuthorizationInfo()方法,获取当前用户的权限,其他的权限验证都是有shiro框架做 ~~~ 进行角色的添加和权限的添加。 authorizationInfo.addRole(role.getRole()); authorizationInfo.addStringPermission(p.getPermission()); ~~~ ~~~ /** * 配置用户权限 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { if (principals == null) { throw new AuthorizationException("PrincipalCollection method argument cannot be null."); } String userId = (String) getAvailablePrincipal(principals); List<String> roleList = roleService.getRoleIdsByUserId(userId); //用户角色获取用户功能id Set<String> roleSet = new HashSet<>(); //角色集合 Set<String> menuSet = new HashSet<>(); //菜单集合 List<String> menus; for(String roleId : roleList){ roleSet.add(roleId); menus = menuService.getMenuidsByRoleId(roleId); Collections.addAll(menuSet,menus.toArray(new String[menus.size()])); } SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(roleSet); authorizationInfo.setStringPermissions(menuSet); return authorizationInfo; } ~~~