您好,登錄后才能下訂單哦!
本篇文章為大家展示了SpringBoot中怎么利用Shiro實現(xiàn)登陸認證和權(quán)限管理,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
Apache Shiro是一個功能強大、靈活的,開源的安全框架。它可以干凈利落地處理身份驗證、授權(quán)、企業(yè)會話管理和加密。
Apache Shiro的首要目標是易于使用和理解。安全通常很復(fù)雜,甚至讓人感到很痛苦,但是Shiro卻不是這樣子的。一個好的安全框架應(yīng)該屏蔽復(fù)雜性,向外暴露簡單、直觀的API,來簡化開發(fā)人員實現(xiàn)應(yīng)用程序安全所花費的時間和精力。
驗證用戶身份
用戶訪問權(quán)限控制,比如:1、判斷用戶是否分配了一定的安全角色。2、判斷用戶是否被授予完成某個操作的權(quán)限
在非 web 或 EJB 容器的環(huán)境下可以任意使用Session API
可以響應(yīng)認證、訪問控制,或者 Session 生命周期中發(fā)生的事件
可將一個或以上用戶安全數(shù)據(jù)源數(shù)據(jù)組合成一個復(fù)合的用戶 “view”(視圖)
支持單點登錄(SSO)功能
支持提供“Remember Me”服務(wù),獲取用戶關(guān)聯(lián)信息而無需登錄
等等——都集成到一個有凝聚力的易于使用的API。
Shiro 致力在所有應(yīng)用環(huán)境下實現(xiàn)上述功能,小到命令行應(yīng)用程序,大到企業(yè)應(yīng)用中,而且不需要借助第三方框架、容器、應(yīng)用服務(wù)器等。當(dāng)然 Shiro 的目的是盡量的融入到這樣的應(yīng)用環(huán)境中去,但也可以在它們之外的任何環(huán)境下開箱即用。
Authentication(認證), Authorization(授權(quán)), Session Management(會話管理), Cryptography(加密)被 Shiro 框架的開發(fā)團隊稱之為應(yīng)用安全的四大基石。那么就讓我們來看看它們吧:
Authentication(認證):用戶身份識別,通常被稱為用戶“登錄”
Authorization(授權(quán)):訪問控制。比如某個用戶是否具有某個操作的使用權(quán)限。
Session Management(會話管理):特定于用戶的會話管理,甚至在非web 或 EJB 應(yīng)用程序。
Cryptography(加密):在對數(shù)據(jù)源使用加密算法加密的同時,保證易于使用。
還有其他的功能來支持和加強這些不同應(yīng)用環(huán)境下安全領(lǐng)域的關(guān)注點。特別是對以下的功能支持:
Web支持:Shiro 提供的 web 支持 api ,可以很輕松的保護 web 應(yīng)用程序的安全。
緩存:緩存是 Apache Shiro 保證安全操作快速、高效的重要手段。
并發(fā):Apache Shiro 支持多線程應(yīng)用程序的并發(fā)特性。
測試:支持單元測試和集成測試,確保代碼和預(yù)想的一樣安全。
“Run As”:這個功能允許用戶假設(shè)另一個用戶的身份(在許可的前提下)。
“Remember Me”:跨 session 記錄用戶的身份,只有在強制需要時才需要登錄。
注意: Shiro不會去維護用戶、維護權(quán)限,這些需要我們自己去設(shè)計/提供,然后通過相應(yīng)的接口注入給Shiro
User(用戶)實體類創(chuàng)建,本例使用了Lombok插件,通過@Data注解減少了對GET/SET方法的生成。
@Data @Entity @Table(name = "user") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private String username; private String password; private String salt; @Column(name = "is_deleted",length = 2) private int isDeleted; //刪除標記 0未刪除 1已刪除 @Column(name = "gmt_create",updatable = false) private LocalDateTime createAt; @Column(name = "gmt_modified") private LocalDateTime updateAt; @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "user_to_role", joinColumns = { @JoinColumn(name = "user_id") }, inverseJoinColumns = { @JoinColumn(name = "role_id") }) private List<SysRole> roles; public String getCredentialsSalt() { return username + salt + salt; } }
用戶對應(yīng)角色的對應(yīng)關(guān)系聲明為多對多關(guān)聯(lián),其中關(guān)聯(lián)表實體可以在源代碼中查看,此處忽略。
Role(角色)實體類創(chuàng)建:
@Data @Entity @Table(name = "role") public class SysRole { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private String role; private String roleName; @Column(name = "is_deleted",length = 2) private int isDeleted; //刪除標記 0未刪除 1已刪除 @CreatedDate @Column(name = "gmt_create") private LocalDateTime createAt; @LastModifiedDate @Column(name = "gmt_modified") private LocalDateTime updateAt; @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "role_to_permission", joinColumns = { @JoinColumn(name = "role_id") }, inverseJoinColumns = { @JoinColumn(name = "perm_id") }) private List<SysPermission> permissions; }
Permission(權(quán)限)實體類創(chuàng)建:
@Data @Entity @Table(name = "permission") public class SysPermission { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String code; private String name; @Column(name = "is_deleted",length = 2) private int isDeleted; //刪除標記 0未刪除 1已刪除 @CreatedDate @Column(name = "gmt_create") private LocalDateTime createAt; @LastModifiedDate @Column(name = "gmt_modified") private LocalDateTime updateAt; }
本例使用JPA作為數(shù)據(jù)庫持久層框架,因此我們將建表的任務(wù)交給框架自動完成,我們只需要在entity中寫清楚對應(yīng)關(guān)系即可。源碼所給的struts.sql文件也詳細說明了DDL語句。
Shiro的重點在于對應(yīng)的config文件的配置以及對于Realm的實現(xiàn)。
ShiroConfig的實現(xiàn)方式,在Shiro中,shirFilter會對配置的路徑進行過濾,過濾的級別如下:
/** * shiro的內(nèi)置過濾器 * anon:無需認證就可以訪問 默認 * authc:必須認證了才能訪問 * user:必須擁有記住我功能才能訪問 * perms:必須擁有對某個的權(quán)限才能訪問 * role:擁有某個角色權(quán)限才能訪問 * ilter工廠,設(shè)置對應(yīng)的過濾條件和跳轉(zhuǎn)條件 */
@Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shirFilter(DefaultWebSecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String, String> filterChainDefinitionMap = new HashMap<>(); shiroFilterFactoryBean.setLoginUrl("/login"); shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized"); shiroFilterFactoryBean.setSuccessUrl("/home/index"); filterChainDefinitionMap.put("/*", "anon"); filterChainDefinitionMap.put("/authorized/index", "authc"); filterChainDefinitionMap.put("/authorized/admin", "roles[admin]"); filterChainDefinitionMap.put("/authorized/add", "perms[Create,Update]"); filterChainDefinitionMap.put("/authorized/delete", "perms[Delete]"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); hashedCredentialsMatcher.setHashAlgorithmName(PasswordHelper.ALGORITHM_NAME); // 散列算法 hashedCredentialsMatcher.setHashIterations(PasswordHelper.HASH_ITERATIONS); // 散列次數(shù) return hashedCredentialsMatcher; } //注入自己的realm @Bean public CustomizedRealm shiroRealm() { CustomizedRealm shiroRealm = new CustomizedRealm(); shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher()); //設(shè)置加密的方式 return shiroRealm; } @Bean public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(shiroRealm()); return securityManager; } @Bean public PasswordHelper passwordHelper() { return new PasswordHelper(); } //加入注解的使用,不加入這個注解不生效 @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } }
自定義Realm的實現(xiàn):
public class CustomizedRealm extends AuthorizingRealm{ @Autowired private UserService userService; /** * 授權(quán) * 只有當(dāng)需要檢測用戶權(quán)限的時候才會調(diào)用此方法,例如checkRole,checkPermission之類的 * @param principals * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); String username = (String) principals.getPrimaryPrincipal(); User user = userService.findUserByName(username); if(user == null) return null; for (SysRole role : user.getRoles()) { authorizationInfo.addRole(role.getRole()); for (SysPermission permission : role.getPermissions()) { authorizationInfo.addStringPermission(permission.getName()); } } return authorizationInfo; } /** * 認證 * 默認使用此方法進行用戶名正確與否驗證,錯誤拋出異常即可。 * @param token * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String) token.getPrincipal(); User user = userService.findUserByName(username); if(user == null) throw new AuthenticationException(ErrorCode.USER_NOT_FOUND.getMsg()); SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), ByteSource.Util.bytes(user.getCredentialsSalt()), getName()); return authenticationInfo; } }
簡單的控制層邏輯:
@RestController @RequestMapping @Slf4j public class UserController { @Autowired private UserService userService; @Autowired private PasswordHelper passwordHelper; @GetMapping("doLogin") public String login(@RequestParam String username, @RequestParam String password) { UsernamePasswordToken token = new UsernamePasswordToken(username, password); Subject subject = SecurityUtils.getSubject(); try { subject.login(token); } catch (IncorrectCredentialsException ice) { throw new AuthorizationException(ErrorCode.USER_PASSWORD_ERROR.getMsg()); } catch (UnknownAccountException uae) { throw new AuthorizationException(ErrorCode.IDENTIFICATION_ERROR.getMsg()); } //登陸成功,將用戶信息放入session中 User user = userService.findUserByName(username); subject.getSession().setAttribute("currentUser", user); return "LOGIN SUCCEED"; } @GetMapping("register") public String register(@RequestParam String username, @RequestParam String password) { User user = new User(); user.setUsername(username); user.setPassword(password); passwordHelper.encryptPassword(user); userService.saveUser(user); return "SUCCESS"; } @GetMapping("test") @RequiresRoles(value = {"USER"}) //需要USER角色才能訪問 @RequiresPermissions(value = {"QUERY","ADD"},logical = Logical.OR) //多個權(quán)限的寫法 public String test(@CurrentUser User user) { //@CurrentUser 自定義注解實現(xiàn),可以參考CurrentUser.java,其實現(xiàn)過程在CurrentUserMethodArgumentResolver類中 log.info("{}",user.getRoles()); return user.getUsername(); } }
上面的例子中,使用@RequiresRoles的注解,需要在ShiroConfig配置AuthorizationAttributeSourceAdvisor,否則注解無法生效。
1.我們創(chuàng)建了有個用戶,張三,其擁有User的權(quán)限,先通過登錄;(沒有用戶的可以調(diào)用register方法先創(chuàng)建用戶,再配置對應(yīng)的權(quán)限關(guān)聯(lián)關(guān)系)
2.此處編寫了有個test方法,一開始我們設(shè)置需要user權(quán)限可以訪問成功,如下:
@GetMapping("test") @RequiresRoles(value = {"USER"}) //需要USER角色才能訪問 @RequiresPermissions(value = {"QUERY","ADD"},logical = Logical.OR) //多個權(quán)限的寫法 public String test(@CurrentUser User user) { //@CurrentUser 自定義注解實現(xiàn),可以參考CurrentUser.java,其實現(xiàn)過程在CurrentUserMethodArgumentResolver類中 log.info("{}",user.getRoles()); return user.getUsername(); } //@RequiresRoles(value = {"USER"}) zhangsan有用user權(quán)限,登陸成功
權(quán)限沒有問題。
3.修改此處登錄的權(quán)限訪問為,ADMIN級別
@GetMapping("test") @RequiresRoles(value = {"ADMIN"}) //需要ADMIN角色才能訪問 @RequiresPermissions(value = {"QUERY","ADD"},logical = Logical.OR) //多個權(quán)限的寫法 public String test(@CurrentUser User user) { log.info("{}",user.getRoles()); return user.getUsername(); } //@RequiresRoles(value = {"ADMIN"}) zhangsan未擁有ADMIN權(quán)限,被攔截
上述內(nèi)容就是SpringBoot中怎么利用Shiro實現(xiàn)登陸認證和權(quán)限管理,你們學(xué)到知識或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識儲備,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。