溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

SpringBoot中怎么利用Shiro實現(xiàn)登陸認證和權(quán)限管理

發(fā)布時間:2021-07-08 16:43:31 來源:億速云 閱讀:371 作者:Leah 欄目:大數(shù)據(jù)

本篇文章為大家展示了SpringBoot中怎么利用Shiro實現(xiàn)登陸認證和權(quán)限管理,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。

Shiro是什么?

Apache Shiro是一個功能強大、靈活的,開源的安全框架。它可以干凈利落地處理身份驗證、授權(quán)、企業(yè)會話管理和加密。

Apache Shiro的首要目標是易于使用和理解。安全通常很復(fù)雜,甚至讓人感到很痛苦,但是Shiro卻不是這樣子的。一個好的安全框架應(yīng)該屏蔽復(fù)雜性,向外暴露簡單、直觀的API,來簡化開發(fā)人員實現(xiàn)應(yīng)用程序安全所花費的時間和精力。

Shiro能做什么呢?
  • 驗證用戶身份

  • 用戶訪問權(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)境下開箱即用。

Shiro有哪些組成?

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)系)

SpringBoot中怎么利用Shiro實現(xiàn)登陸認證和權(quá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)限,登陸成功

SpringBoot中怎么利用Shiro實現(xiàn)登陸認證和權(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)限,被攔截

SpringBoot中怎么利用Shiro實現(xiàn)登陸認證和權(quán)限管理

上述內(nèi)容就是SpringBoot中怎么利用Shiro實現(xiàn)登陸認證和權(quán)限管理,你們學(xué)到知識或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識儲備,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責(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)容。

AI