一.使用的spring boot +mybatis-plus+shiro+maven來搭建項目框架 1 <!--shiro--> 2 <dependency> 3 <groupId>org.apache.shiro</groupId> 4 <artifactId>shiro-core</artifa ...
一.使用的spring boot +mybatis-plus+shiro+maven來搭建項目框架
1 <!--shiro--> 2 <dependency> 3 <groupId>org.apache.shiro</groupId> 4 <artifactId>shiro-core</artifactId> 5 <version>1.4.0</version> 6 </dependency> 7 <dependency> 8 <groupId>org.apache.shiro</groupId> 9 <artifactId>shiro-spring</artifactId> 10 <version>1.4.0</version> 11 </dependency>
2.寫一個登錄頁面(登錄頁面代碼就自己隨便寫一個form表單提交到controller就行)
3.在controller中創建userLogin方法,創建UsernamePasswordToken,獲取subject,通過subject.login來進行登錄認證。
1 @Slf4j 2 @RestController 3 @RequestMapping("/sys-user") 4 public class SysUserController { 5 6 /** 7 * 用戶登錄 8 * @param userName 9 * @param password 10 */ 11 @PostMapping(value = "/login") 12 public ServerResponse userLogin(@RequestParam String userName, @RequestParam String password) 13 { 14 //1.獲取token 15 UsernamePasswordToken token = new UsernamePasswordToken(userName,password); 16 //2.獲取subject 17 Subject subject = SecurityUtils.getSubject(); 18 //3.進行登錄 19 try { 20 subject.login(token); 21 log.info("subject:"+subject.getPrincipal().toString()); 22 return ServerResponse.createBySuccessMessage("登錄成功!"); 23 }catch (Exception e) 24 { 25 log.error("登錄失敗,用戶名[{}]", userName, e); 26 token.clear(); 27 return ServerResponse.createByErrorMessage(e.getMessage()); 28 } 29 }
以上就是一個基本的登錄流程,下麵就繼續分析subject.login()方法,到底怎麼實現登錄認證的,在後續中逐步分析如何使用自定義的Realm和CredentialsMatcher密碼比較器.
首先,我們從外部來看 Shiro 吧,即從應用程式角度的來觀察如何使用 Shiro 完成工作。如下圖:(引用自《跟我學shiro教程》)
4.通過代碼跟蹤可以發現,subject.login()方法又調用了securityManager.login()方法,因此我們還需要一個註冊一個securityManager的bean交給spring去管理
5.創建一個config的package,便於管理,項目結構如下
6.創建一個ShiroConfig的類,用來配置shiro相關的bean,首先使用@Configuration註解表明這是一個配置類,並註冊一個securityManager的bean,發現傳入參數是一個MyRealm的類,這個類就是我們需要自己去定義的Realm類
1 //配置核心安全事務管理器 2 /** 3 * securityManager 4 * @param authRealm ,@Qualifier表明瞭哪個實現類才是我們所需要的 5 * @return 6 */ 7 @Bean(name="securityManager") 8 public SecurityManager securityManager(@Qualifier("myRealm") MyRealm authRealm) { 9 10 DefaultSecurityManager securityManager = new DefaultWebSecurityManager(); 11 12 //設置Realm 13 securityManager.setRealm(authRealm); 14 securityManager.setRememberMeManager(rememberMeManager()); 15 return securityManager; 16 } 17 //配置自定義的許可權登錄器 18 @Bean 19 public MyRealm myRealm() 20 { 21 MyRealm myRealm = new MyRealm(); 22 myRealm.setCredentialsMatcher(new CredentialsMatcher()); 23 return myRealm; 24 }
7.創建類Realm類並繼承AuthorizingRealm類,然後通過token中的Principal(即用戶名)去查詢資料庫中User,然後再把查詢到的用戶信息(包括密碼)返回AuthorizationInfo
自定義MyRealm類繼承thorizingRealm類,並且重寫doGetAuthenticationInfo方法
1 public class MyRealm extends AuthorizingRealm{ 2 3 @Autowired 4 private SysUserServiceImpl sysUserService; 5 //授權 6 @Override 7 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 8 return null; 9 } 10 11 //認證 12 @Override 13 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { 14 /**獲取用戶輸入的用戶信息*/ 15 String userName = (String)token.getPrincipal(); 16 QueryWrapper<SysUser> queryWrapper = new QueryWrapper<SysUser>(); 17 18 19 queryWrapper.eq("username",userName); 20 21 SysUser user = sysUserService.getOne(queryWrapper); 22 23 if(user == null) 24 { 25 throw new UnknownAccountException("該用戶不存在"); 26 } 27 if(user.getStatus() != null && Const.UserStatusEnum.DISABLE.getCode().equals(user.getStatus())) 28 { 29 throw new LockedAccountException("該賬號被鎖定,請聯繫管理員!"); 30 } 31 //把user信息放在session中 32 SecurityUtils.getSubject().getSession().setAttribute(Const.CURRENT_USER,user); 33 return new SimpleAuthenticationInfo(user,user.getPassword(), ByteSource.Util.bytes(userName),getName()); 34 } 35 }
如果身份認證失敗就會捕獲AuthenticationException,常見的如下:
DisabledAccountException(禁用的帳號)
LockedAccountException(鎖定的帳號)
UnknownAccountException(錯誤的帳號)
ExcessiveAttemptsException(登錄失敗次數過多)
IncorrectCredentialsException (錯誤的憑證)
ExpiredCredentialsException(過期的憑證)等
如果身份認證通過後就要進行密碼認證
自定義一個CredentialsMatcher類繼承SimpleCredentialsMatcher類,並且重寫doCredentiaIsMatch方法
以上就是shiro的基本登錄認證流程,如有不當之處還望大家多多指教。