工作流程:
瀏覽器將用戶名、密碼、是否記住登錄等信息發送給登錄controller ,
new UsernamePasswordToken()獲取token,將用戶名、加密後的密碼、rememberMe,set到token中。SecurityUtils.getSubject();獲取subject對象,執行subect.login(token)進行登錄操作並捕獲可能出現的賬號密碼錯誤等異常。
1.前端登錄頁面將用戶名、密碼、是否記住登錄,傳入到Controller
Controller登錄方法:
//1.獲取shiro中的subject對象
Subject subject = SecurityUtils.getSubject();
//2.對用戶從頁面輸入的密碼進行加密處理
password = new Md5Hash(password,username,1024).toString();
System.out.println("加密後的密碼: " + password);
//3.創建shiro中的用戶名和密碼對象,將用戶輸入的用戶名密碼交給shiro管理
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//4.關於記住我的設置
System.out.println("是否記住我: " + rememberMe);
if(rememberMe){
token.setRememberMe(true);
}
//5.調用shiro的登錄方法,調用後,shiro會自動執行Realm實現類===========
try {
subject.login(token);
System.out.println("開始登陸");
} catch (UnknownAccountException e) {
return new Result(1, "賬號不存在");
}catch (IncorrectCredentialsException e){
return new Result(1,"密碼錯誤");
}catch (AuthenticationException e){
System.out.println("其他異常");
return new Result(1,"其他異常");
}
return new Result(0, "登陸成功!");
/**
* 註銷方法
* @return
*/
public Result logout(){
//1.獲取subject對象
Subject subject = SecurityUtils.getSubject();
//2.返回註銷方法
subject.logout();
//3.返回
return Result.ok("註銷成功");
}
獲取完數據後,shiro會自動執行Realm的實現類,實現類需要手動實現,如下:
/**
* projectName: myproject
* @author: xxx
* time: 2021/9/6 21:17
* description: 自定義realm對象
*
* 1.從數據庫根據用戶名,取出數據庫中的用戶名,密碼交給shiro框架
* 2.根據用戶名,到數據庫取出用戶對應的角色和權限對象交給shiro框架管理
*/
public class MyUserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Autowired
private MenuService menuService;
@Autowired
private RoleService roleService;
/**
* 用戶輸入的用戶名和密碼輸入正確,校驗完成後進行賦值操作
* 根據用戶名到數據庫查詢這個用戶對應的角色和權限,交給shiro管理
* 調用時機:在需要訪問資源的時候,需要角色和權限的視乎纔會調用此方法
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("已登陸成功,授予登錄用戶權限,也就是賦予用戶對應的角色和能夠訪問的菜單");
//1.獲取當前登錄的用戶對象
SysUsers sysUSers = (SysUsers) principalCollection.getPrimaryPrincipal();
//2.獲取當前登錄用戶的 用戶id
int uid = sysUSers.getId();
//3.根據用戶id,查詢數據庫中這個用戶對應的角色集合和權限集合
Set<String> roleList = roleService.findRoleListByUid(uid);
Set<String> menuList = menuService.findMenuListbyUid(uid);
//4.創建shiro中的用戶權限對象
SimpleAuthorizationInfo auth = new SimpleAuthorizationInfo();
//5.將查詢到的角色集合放入shiro的權限對象
auth.setRoles(roleList);
//6.將查詢到的權限集合放入shiro的權限對象
auth.setStringPermissions(menuList);
//7.返回shiro權限對象
return auth;
}
/**
* 根據用戶在頁面輸入的用戶名,查詢數據庫中的用戶名和密碼,交給shiro框架
* 讓shiro框架進行對比用戶名,密碼是否正確
* 調用時機:在controller調用subject.login(token);方法就會執行這個方法,進行用戶名 密碼校驗
* @param auth
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
System.out.println("開始校驗用戶名和密碼................");
//1.獲取用戶在瀏覽器輸入的用戶名
String userName = (String) auth.getPrincipal();
//2.根據用戶輸入的用戶名,查詢數據庫的用戶對象
String password = new String((char[]) auth.getCredentials());
//3.判斷用戶是否爲空,爲空則拋出異常
SysUsers sysUSer = userService.findUserbyName(userName);
if (sysUSer == null) {
throw new UnknownAccountException("賬號不存在,請先註冊,再登錄!");
}
//4.對比數據庫的密碼和用戶輸入的密碼是否一致
if(!password.equals(sysUSer.getPassword())){
throw new IncorrectCredentialsException("密碼錯誤");
}
//5.判斷用戶狀態,1正常,其他爲鎖定狀態
if(sysUSer.getStatus() != 1){
throw new LockedAccountException("賬號被鎖定不允許登錄");
}
//6.封裝shiro中需要的權限對象,包括用戶名 密碼 以及當前用戶對象交給shiro返回
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(userName, password, this.getName());
return info;
}
}
其他Controller方法需要做權限驗證時,可以在上面加註解@RequiresPermissions("sys:product:add")
括號裏是權限字符串,用冒號連接
shiro註解用在Service和Controller層,但是如果Service層有事務註解,那麼shiro註解要放在Controller層。因爲兩個代理對象在類型轉換時會出現異常。
https://www.cnblogs.com/huangruiwu/p/15410382.html
2、接口獲取用戶名,密碼生成UsernamePasswordToken並提交認證:
@Controller
@RequestMapping("/api")
public class ApiController {
@Autowired
SysMenuService sysMenuService;
@RequestMapping("/login")
@ResponseBody
public ResponseVO login(String username, String password) {
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//獲取當前的Subject
Subject currentUser = SecurityUtils.getSubject();
try {
// 在調用了login方法後,SecurityManager會收到AuthenticationToken,並將其發送給已配置的Realm執行必須的認證檢查
// 每個Realm都能在必要時對提交的AuthenticationTokens作出反應
// 所以這一步在調用login(token)方法時,它會走到xxRealm.doGetAuthenticationInfo()方法中,具體驗證方式詳見此方法
currentUser.login(token);
//認證通過,將菜單信息返回給前端展示
if (currentUser.isAuthenticated()) {
MenuSystem menuSystem = sysMenuService.findMenuTree();
Gson gson = new Gson();
String menuJson = gson.toJson(menuSystem, MenuSystem.class);
return ResultUtil.success("登錄成功!", menuJson);
}
} catch (UnknownAccountException e1) {
e1.printStackTrace();
return ResultUtil.error("用戶名不存在!");
} catch (IncorrectCredentialsException e2) {
e2.printStackTrace();
return ResultUtil.error("密碼輸入錯誤!");
} catch (Exception e) {
e.printStackTrace();
return ResultUtil.error(e.getMessage());
}
return ResultUtil.error("登錄失敗!");
}
說明:這裏的菜單數據並沒有使用shiro標籤來實現,因爲layuimini的菜單渲染是通過json數據的形式來實現的,所以這裏我也是將菜單數據轉換成了json給前端。
如果認證通過,那麼通過subject.isAuthenticated()返回的結果就會是true,我們就可以認爲登錄成功了,此時,可以就可以將對應的菜單信息傳回給前端渲染展示了。
https://blog.csdn.net/superyu1992/article/details/125915866
https://blog.csdn.net/superyu1992/article/details/126014248
https://blog.csdn.net/superyu1992/article/details/126058351
https://blog.51cto.com/wyait/2082803