依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.2</version>
</dependency>
自定义用户验证
-
创建
MyDBRealm
类,继承org.apache.shiro.realm.AuthorizingRealm
-
实现
doGetAuthenticationInfo()
方法,该方法就是验证时获取用户验证信息的核心方法下面的方法实现了从数据库获取用户验证信息的逻辑:
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { // 从token中获取传入的Principal(用户身份信息) String userName = (String)authenticationToken.getPrincipal(); // 根据用户身份信息在数据库中查询用户信息的验证数据(密码) Map<String,Object> query = new HashMap<>(); query.put("user_name",userName); List<Employee> emps = employeeMapper.selectByMap(query); if (emps == null || emps.size() == 0){ return null; } Employee emp = emps.get(0); /* * 返回一个包含用户信息(可以是用户名或者是一个用户对象,作为Principal)、数据库中的验证数据的SimpleAuthenticationInfo对象 * 这个对象会在后续的Credentials Matching(证书匹配)过程中将token中的credential和从数据库查到的credential进行对比 * 匹配则验证成功,不匹配则验证失败 * */ SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(emp,emp.getPassword(),getName()); return simpleAuthenticationInfo; }
-
配置Shiro
@Configuration public class ShiroConfig { @Bean public SecurityManager securityManager(){ DefaultSecurityManager securityManager = new DefaultSecurityManager(); securityManager.setRealm(myRealm()); return securityManager; } @Bean public Realm myRealm(){ MyDBRealm myDBRealm = new MyDBRealm(); return myDBRealm; } }
-
使用
public void test(){ SecurityUtils.setSecurityManager(securityManager); Subject currentUser = SecurityUtils.getSubject(); if (!currentUser.isAuthenticated()){ UsernamePasswordToken token = new UsernamePasswordToken("admin@admin.com","111111"); try { currentUser.login(token); log.info("登录成功"); } catch (UnknownAccountException uae) { log.info("There is no user with username of " + token.getPrincipal()); } catch (IncorrectCredentialsException ice) { log.info("Password for account " + token.getPrincipal() + " was incorrect!"); } catch (LockedAccountException lae) { log.info("The account for username " + token.getPrincipal() + " is locked. " + "Please contact your administrator to unlock it."); } // ... catch more exceptions here (maybe custom ones specific to your application? catch (AuthenticationException ae) { //unexpected condition? error? } currentUser.logout(); } }
-
这里的credential比对是默认的简单等值比对,我们可以创建自己的
CredentialsMatcher
,然后将其配置到Realm
中public class MyCredentialsMatcher implements CredentialsMatcher { /** * @Description 对md5加密后的Credential进行校验 * @Param [authenticationToken:登录表单提交的用户验证信息, authenticationInfo:从doGetAuthenticationInfo方法获取大的realm中的用户验证信息] * @Return boolean * @Author Lrz * @date 2020/8/24 15:32 */ @Override public boolean doCredentialsMatch(AuthenticationToken authenticationToken, AuthenticationInfo authenticationInfo) { UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken; String pwd = new String(token.getPassword()); String md5Pwd = DigestUtils.md5DigestAsHex(pwd.getBytes()); return authenticationInfo.getCredentials().equals(md5Pwd); } }
@Bean public Realm myRealm(){ MyDBRealm myDBRealm = new MyDBRealm(); myDBRealm.setCredentialsMatcher(new MyCredentialsMatcher()); return myDBRealm; }
这样,应用会将提供的Credential进行MD5加密后再与数据库中的数据进行对比。
自定义授权
-
在上面创建的
MyRealm
类中实现doGetAuthorizationInfo
方法:protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { Employee employee = (Employee) principalCollection.getPrimaryPrincipal(); // 获取用户角色信息 List<Role> roleList = roleMapper.getRolesByEmpNo(employee.getEmpNo()); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); Set<String> roles = new HashSet<>(); for (Role role : roleList){ roles.add(role.getRoleName()); } authorizationInfo.setRoles(roles); //获取用户权限信息 List<String> permissions = permissionMapper.selectByRoles(roleList); authorizationInfo.setStringPermissions(new HashSet<String>(permissions)); return authorizationInfo; }
这个方法中,根据当前用户信息获取用户的角色列表和权限列表。
基于角色的授权
Subject currentUser = SecurityUtils.getSubject(); if (currentUser.hasRole("admin")){ // 验证用户是否是admin }
基于权限的授权
Subject currentUser = SecurityUtils.getSubject(); if (currentUser.isPermitted("read")){ // 验证用户是否有read权限 }
与SpringBoot Web的集成
除了上面的定义之外,还需要一个注册一个ShiroFilterChainDefinition
Bean