Shiro安全框架快速使用

1.shiro安全框架

1.shiro快速入门:

官网地址: https://github.com/apache/shiro/blob/master/samples/quickstart/src/main/resources/shiro.ini

2.先搭建springboot的运行环境

3.导入shiro-spring依赖

  • shiro三大对象:
    • Subject : 当前用户
    • SecurityManager : 管理所有用户
    • Realm : 连接数据
<!-- 整合shiro -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.5.1</version>
</dependency>

4.Spring整合shiro开始

1.导入依赖

去官网下载最新版 有助于学习进步

<!-- 整合shiro -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.5.1</version>
</dependency>
  1. 开始配置

src/main/java/com/kuang/config/ShiroConfig.java

在配置文件中,有三大要素

  • ShiroFilterFactoryBean (过滤器) 3. 最后连接到前端,设置安全管理器

  • DefaultWebSecurityManager 2. 再接管对象,关联realm

  • 创建 realm 对象 , 需要自定义 1. 先创建对象

    1.先创建对象

    我们要创建一个realm类来自定义Realm,再将我们自定义的realm放在配置文件中托管

    我们自定义的realm需要继承 AuthorizingRealm

    重写里边的两个方法,授权和认证 (AuthorizationInfo授权(AuthenticationInfo认证)

    src/main/java/com/kuang/config/UserRealm

    public class UserRealm extends AuthorizingRealm {
        /*授权*/
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            System.out.println("来到了授权===》》》AuthorizationInfo");
            return null;
        }
        /*认证*/
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            System.out.println("来到了认证===》》》AuthenticationInfo");
            return null;
        }
    }
    

    再将自己写的Realm注入到Bean中

    src/main/java/com/kuang/config/ShiroConfig.java

    //realm
    @Bean
    public UserRealm userRealm(){
        return new UserRealm();
    }
    2.再接管对象

src/main/java/com/kuang/config/ShiroConfig.java

​ 这里我们需要思考一个问题:

​ 我们如何将我们自定义的realm交给管理器呢? 直接调用realm方法吗?

​ 不,我们可以使用**@Qualifier(“name”)注解来绑定我们自己写的realm,注解里的name为我们返回realm方法的名称,还有另一种方法,我们@Qualifier(“name”)注解的name可以写Bean里边的name名称/@Bean(name=“userRealm”)**

 //DefaultWebSecurityManager
@Bean
public WebSecurityManager getWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
    DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
    manager.setRealm(userRealm);
    return manager;
}
//realm
//@Bean(name="userRealm")
@Bean
public UserRealm userRealm(){
    return new UserRealm();
}
3.再连接到前端

下边是我们 src/main/java/com/kuang/config/ShiroConfig.java的总内容

@Configuration
public class ShiroConfig {
    //ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getWebSecurityManager") WebSecurityManager getWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(getWebSecurityManager);
        return shiroFilterFactoryBean;

    }
    //DefaultWebSecurityManager
    @Bean
    public WebSecurityManager getWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(userRealm);
        return manager;
    }

    //realm
    //@Bean(name="userRealm")
    @Bean
    public UserRealm userRealm(){
        return new UserRealm();
    }
}

3.shiro里边的内容写好了,开始搭建我们的环境吧。

用controller跳转页面即可,这里省略。

4.环境搭建好之后,我们来实现我们的请求拦截吧!

我们目前拥有的页面

/templates/index.html

/templates/login.html

/templates/user/add.html

/templates/user/update.html

好了,我们想要是实现我们的user里边的添加和更新页面只有认证之后才可以访问,那么怎么办呢?

打开我们shiro的配置类: src/main/java/com/kuang/config/ShiroConfig.java

来比较与上边的配置类多了哪些东西?

只是多了过滤器是吧,我们使用bean来设置我们的过滤请求和登录请求

bean.setFilterChainDefinitionMap(filterChainDefinitionMap); // 添加一个map,key值放路径,value值放权限,可以在源码中进行查看

bean.setLoginUrl("/toLogin"); //当我们没有权限的话,我们会来到这里设置的路径,注意用controller拦截,再跳转。

@Configuration
public class ShiroConfig {
    //ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getWebSecurityManager") WebSecurityManager getWebSecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(getWebSecurityManager);

        /*
           anon:无需认证就可以访问
           authc:必须认证了才能访问
           user:必须拥有  记住我   功能才使用
           perms:拥有对某个资源的权限才能访问
           role:拥有与某个角色才能访问
         */

        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();

        filterChainDefinitionMap.put("/user/*","authc");

        /*设置过滤请求*/
        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        bean.setLoginUrl("/toLogin");

        return bean;

    }
    //DefaultWebSecurityManager
    @Bean
    public WebSecurityManager getWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(userRealm);
        return manager;
    }

    //realm
    @Bean
    public UserRealm userRealm(){
        return new UserRealm();
    }
}

5.好的,那接下来我们开始进行认证

这里只写后端代码,前端代码可根据后端代码写出。

我们的认证,是在我们的realm中进行认证的,但首先我们需要先接受到我们的用户名和密码,并且将用户名和密码封装在令牌中(token),然后传递给token。

src/main/java/com/kuang/controller/helloController.java

拦截我们form表单提交的请求。

@RequestMapping("/login")
public String login(String username,String password,Model model){
    /*先获取当前的用户*/
    Subject subject = SecurityUtils.getSubject();
    /*封装用户的信息*/
    UsernamePasswordToken token = new UsernamePasswordToken(username,password);
    try {
        /*执行登录方法*/
        subject.login(token);
        return "index";
    }catch (UnknownAccountException e){
        model.addAttribute("msg","用户名不存在!");
        return "login";
    }catch (IncorrectCredentialsException e){
        model.addAttribute("msg","密码不存在!");
        return "login";
    }
}

然后在我们自定义的realm中进行认证

src/main/java/com/kuang/config/UserRealm.java

public class UserRealm extends AuthorizingRealm {
    /*授权*/
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("来到了授权===》》》AuthorizationInfo");
        return null;
    }
    /*认证*/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("来到了认证===》》》AuthenticationInfo");

        String username = "root";
        String passwrod = "123456";

        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        /*如果用户名不一致*/
        if (!userToken.getUsername().equals(username)){
            return null;  /*return null会自动抛出异常UnknownAccountException*/
        }
        /*密码认证不用我们做,shiro做~*/

        return new SimpleAuthenticationInfo("",passwrod,"");
    }
}

2.shiro整合mybatis

1.导入依赖mybatis 和 MySQL驱动

<!--整合上mybatis-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
   <groupId>log4j</groupId>
   <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.12</version>
</dependency>
<!--引入mybatis,这是mybatis官方提供的适配springboot的,而不是springboot自己的-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.0</version>
</dependency>

2. 我们的realm和数据库整合在一起使用

src.main.java.com.kuang.config.UserRealm

我们使用service层,查出数据,来进行验证即可,很简单。

public class UserRealm extends AuthorizingRealm {

    @Autowired
    UserServiceImpl userService;

    /*授权*/
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("来到了授权===》》》AuthorizationInfo");
        return null;
    }
    /*认证*/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("来到了认证===》》》AuthenticationInfo");



        UsernamePasswordToken userToken = (UsernamePasswordToken) token;

        User user = userService.queryUser(userToken.getUsername());
        if(user == null){
            /*没有该用户*/  /*return null会自动抛出异常UnknownAccountException*/
            return null;
        }
//        /*如果用户名不一致*/
//        if (!userToken.getUsername().equals(username)){
//            return null;  /*return null会自动抛出异常UnknownAccountException*/
//        }
        /*密码认证不用我们做,shiro做~*/


        return new SimpleAuthenticationInfo("",user.getPassword(),"");
    }
}

5. 认证搞定了,接下来进行授权

操作

src.main.java.com.kuang.config.ShiroConfig.java中向过滤器中,添加拦截页面,并添加访问所需权限

src.main.java.com.kuang.config.UserRealm中进行授权

先添加拦截页面即所需权限,还是添加到map中即可。

    //ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getWebSecurityManager") WebSecurityManager getWebSecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(getWebSecurityManager);

        /*
           anon:无需认证就可以访问
           authc:必须认证了才能访问
           user:必须拥有  记住我   功能才使用
           perms:拥有对某个资源的权限才能访问
           role:拥有与某个角色才能访问
         */

        Map<String, String> filterMap = new LinkedHashMap<>();

        filterMap.put("/user/add","perms[user:add]");
        filterMap.put("/user/update","perms[user:update]");
        filterMap.put("/user/*","authc");

        /*设置过滤请求*/
        bean.setFilterChainDefinitionMap(filterMap);

        bean.setLoginUrl("/toLogin");
        /*设置没有权限跳转的页面*/
        bean.setUnauthorizedUrl("/unautho");
        return bean;

    }

在UserRealm中进行授权:

授权的时候,我们需要从数据库中取出权限来,但是我们在认证中查询出来的数据如何在授权当中使用呢??

好的,我们在认证中返回身份信息的时候,我们在第一个参数上,传入我们查询的user,那么我们就可以在授权中使用我们查询出来的user对象了。

注意!我们在授权中如何来得到user这个对象呢?

使用 SecurityUtils 工具类,来获得我们的主体subject,通过subject来得到principal,也就是我们的user,对他进行强制类型转化即可。

注意!我们在数据库中写权限这一栏的时候,格式为:user:add user:update

public class UserRealm extends AuthorizingRealm {

    @Autowired
    UserServiceImpl userService;

    /*授权*/
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("来到了授权===》》》AuthorizationInfo");

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        Subject subject = SecurityUtils.getSubject();
        User principal = (User) subject.getPrincipal();
        info.addStringPermission(principal.getPerms());
        return info;
    }
    /*认证*/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("来到了认证===》》》AuthenticationInfo");



        UsernamePasswordToken userToken = (UsernamePasswordToken) token;

        User user = userService.queryUser(userToken.getUsername());
        if(user == null){
            /*没有该用户*/  /*return null会自动抛出异常UnknownAccountException*/
            return null;
        }
//        /*如果用户名不一致*/
//        if (!userToken.getUsername().equals(username)){
//            return null;  /*return null会自动抛出异常UnknownAccountException*/
//        }
        /*密码认证不用我们做,shiro做~*/


        return new SimpleAuthenticationInfo(user,user.getPassword(),"");
    }
}

6.整合thymeleaf - shiro

导入thymeleaf-shiro整合依赖

<!-- thymeleaf-shiro 整合 -->
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>

导入依赖之后,还必须将shiro和thymeleaf交给Bean来管理,之后才可以使用:

@Bean
/*整合ShiroDialect (方言): 用来整合shiro-thymeleaf*/
public ShiroDialect getShiroDialect(){
    return new ShiroDialect();
}

可以导入shiro命名空间

<html lang="en" xmlns:th="http://www.thymeleaf.org"
                xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro"
>

下来,在前端让有权限的话才可以显示:

<div shiro:hasPermission="user:add">
    <a th:href="@{/user/add}">add</a>
</div>

<div shiro:hasPermission="user:update">
    <a th:href="@{/user/update}">update</a>
</div>

7.用户已经登录的话,让登录按钮消失

敲重点!

我们在前端使用thymeleaf取session中的值,可以直接使用session再加上点,把值点出来就好了。

可以获得subject主体,通过subject获得session,再向session中存值,在前端页面中判断是否有值。

向session中存值,可以在认证的时候,认证成功之后,我们向session中存入用户

UserRealm.java

/*向session中存入值,在前边判断是否已经登录,已经登陆的话,登录按钮不显示*/
Subject currentSubject = SecurityUtils.getSubject();
Session session = currentSubject.getSession();
session.setAttribute("loginUser",user);

在前端判断是否为空

<div th:if="${session.loginUser==null}">
    <a th:href="@{thLoginBtn}">登录按钮</a>
</div>

猜你喜欢

转载自blog.csdn.net/qq_45260619/article/details/106447929