Springboot2.x整合shrio进行简单的登录、登出与角色鉴权

Springboot2.x整合shrio进行简单的登录、登出与角色鉴权

首先说明一下,整合shrio主要需要我们独立书写的是配置类与Controller层shrio的核心对象subject,所以这里只贴了配置类与Controller层,而且博主也是才接触到shiro,如果有什么解析的不太对的,还望指出大家一起进步:

首先需要独立引入依赖:

   <!--导入shrio核心依赖-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.2.3</version>
        </dependency>
         <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.4.0</version>
        </dependency>
<!--这个是阿帕奇老铁的工具类,集合判空很好用,还有很多其他功能,反正爽就对了-->
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
        </dependency>
       

Shrio的配置类之AuthRealm:

/**
 * 认证授权的配置类,主要还是与数据源打交道的
 */
public class AuthRealm extends AuthorizingRealm {
    @Autowired
    private UserService userService;


    @Override
    /**
     * 作为授权使用,需要覆盖这个方法来实现
     *
     */
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //授权实际上是在认证登录以后,把获取的User对象放入到了session中
        //将session中的user对象取出来,使用的是principals.fromRealm这个方法
        //传入的参数是这个类的名字,我们使用迭代器,进行遍历,将其取出
        User user= (User) principals.fromRealm(this.getClass().getName()).iterator().next();
        //声明权限的集合
        List<String> permissionList = new ArrayList<>();
        //声明角色的集合
        List<String> roleNameList= new ArrayList<>();
        //拿到当前所有的角色
        Set<Role> roles = user.getRoles();
        //遍历所有的角色
        //实现判断一下角色集合,只有当角色集合不为空的时候才能进行判断
        if (CollectionUtils.isNotEmpty(roles)){
            //将角色挨个遍历出来
            for (Role role: roles){
                //每次遍历角色的时候,将角色名取出来放进提前定义好的角色集合里
                roleNameList.add(role.getRname());
                //再拿到每个角色的权限列表
                Set<Permission> permissions = role.getPermissions();
                if ( CollectionUtils.isNotEmpty(permissions)){
                    for (Permission permission:permissions){
                        //将每一个权限的name放到我们之前定义好的permissionLiST里
                        //因为我们本质上还是根据permissionname进行判断一个用户是否有某个权限
                        permissionList.add(permission.getName());
                    }
                }
            }
        }
        //创建实例zationInfo的实例,将我们获取到的权限加入到对象中
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //将权限授权添加给授权信息实例(SimpleAuthorizationInfo)
        info.addStringPermissions(permissionList);
        //处理simpleAuthorizationInfo的时候,同样为他加上角色授权
        info.addRoles(roleNameList);
        return info;
    }

    @Override
    /**
     * 作为认证登录使用,需要覆盖这个方法来实现
     */
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //1.将传入的token强转为shiro的UsernamePasswordToken类型
        UsernamePasswordToken usernamePasswordToken=(UsernamePasswordToken) token;
        //2.从里面取出对应的Username
        String username = usernamePasswordToken.getUsername();
        //3.调用我们之前写过的根据Username查询User实体的方法,得到User实体
        User user = userService.findByUserName(username);
        //4.将用户里的数据转换成AuthenticationInfo对象
        //传入参数为:刚查询到的user对象,密码,当前这个类的名字(我们通过反射获得)
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user,user.getPassword(),this.getClass().getName());
        //返回就行了
        return simpleAuthenticationInfo;
    }
}

shiro的配置类之CredentialMatcher:

/**
 * 继承shrio为我们提供的Matcher接口,从而实现自定义加密规则的功能,然后再将这个类注入到
 * AuthRealm中就好
 */
public class CredentialMatcher extends SimpleCredentialsMatcher {

    /**
     * 覆盖重写默认的Match的方法
     *
     * @param token
     * @param info
     * @return
     */
    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        //将传递过来的token转化成UsernamePasswordToken
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        //从usernamePasswordToken中取出password
        //但是这个类返回的值是char型数组,我们需要将其转换成string
        //这是前端传递过来的密码
        String password = new String(usernamePasswordToken.getPassword());
        //现在我们需要再拿出我们数据库里的密码,也就是我们刚才为info对象传入的值
        String dbpassword = (String) info.getCredentials();
        //这样两个密码都有了,中间规则就可以由我们来定义了
        //我们这里就直接看它两个是否相同就好了
        //这里的this代指的是SimpleCredentialsMatcher对象
        return this.equals(password, dbpassword);
    }
}

shrio核心配置类也是我们主要进行改写的类:

**
 * 将这个类设置为配置类,springboot启动的时候会自动将她加入到我们的容器中
 */
@Configuration
public class ShiroConfiguraion {

    /**
     * 定义shiroFilter,也就是shiro拦截器
     * 其中需要的参数为:
     * 1.manager安全调度管理者(这里暂时来看只是配置了加密规则)
     * 2.我们自定义的url
     * 3.还有对url自定义的规则
     * @param manager
     * @return
     */
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") SecurityManager manager){
        //实例化一个shrio工厂Bean
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        //注入我们之前定义的mananger
        factoryBean.setSecurityManager(manager);

        //这里之前,基本上前面都是死的,这里就是要配置成我们自己的了

        //定义登录的url(页面)
        factoryBean.setLoginUrl("/login");
        //定义登录成功后跳转到的url(页面)
        factoryBean.setSuccessUrl("/index");
        //定义我们没有权限访问的时候跳转的url(页面)
        factoryBean.setUnauthorizedUrl("/unauthorized");

        //配置我们的请求该怎样进行拦截,也就是该使用什么拦截器进行拦截?
        //key:代表我们访问的请求,value:我们使用什么样的拦截器
        LinkedHashMap<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
        //举例说明
        //第一个参数代表url,第二个代表要求(或者说,如果要访问这个页面需要的权限:比如这里需要用户登录)
        //其中第二个参数是shrio规定的,我们想要赋予什么权限,就要写shrio个我们规定的语言
        //这是DefaultFilter设置的枚举,(可以理解为映射成拦截器)进而对应的不同权限,所以背就好了
        filterChainDefinitionMap.put("/index","authc");
        //这里是初始登录页面,初始页面就是不需要任何校验,任何人都可以访问,用anon标识
        filterChainDefinitionMap.put("/login","anon");
        //这里是注册用户的页面,同样设置为谁都可以访问
        filterChainDefinitionMap.put("/loginUser","anon");
        //设置访问页面的访问权限,这个可以校验角色名称,进而进项权限控制,我们需要先把我们的角色名称放到授权列表里
        filterChainDefinitionMap.put("/admin","roles[管理者]");
        //这个应该是登录的user用户,谁都可以访问,但是具体的user意思我还不是很理解,,,等等学了之后再更新,,
        filterChainDefinitionMap.put("/**","user");



        //这里我们需要将我们配置的拦截规则赋值给factoryBean
        factoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        //返回我们自定义的配置bean对象参数为:
        // 1.manager安全调度管理者(这里暂时来看只是配置了加密规则)
        // 2.我们自定义的url
        // 3.还有对url自定义的规则
        return factoryBean;


    }


    /**
     * 配置总的安全调度管理者,注意这都是shiro家的东西哦
     * @param authRealm
     * @return
     */
    @Bean("securityManager")
    public SecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm){
        //创建一个SecurityManager的实例,安全的总调度管理者
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        //将authRealm放入实例的SecurityManager对象中
        manager.setRealm(authRealm);
        return  manager;
    }

    /**
     * 将最下面定义好的密码比较器通过spring的注解传入到这个配置bean中
     *
     * @param matcher
     * @return
     */
    @Bean("authRealm")
    public AuthRealm authRealm(@Qualifier("credentialMatcher") CredentialMatcher matcher){
        //声明一个authRealm的实例
        AuthRealm authRealm = new AuthRealm();
        //给出密码比较器
        authRealm.setCredentialsMatcher(matcher);
        //返回我们自己定义的authRealm(可以理解为官方的数据源管理者)
        return authRealm;
    }
    /**
     * 为配置类创建配置Bean,交给spring容器
     * 自定义校验规则
     * 也就是密码比较器或者说是采用了什么方式进行加密
     * @return
     */
    @Bean("credentialMatcher")
    public CredentialMatcher credentialMatcher(){
        //返回自定义的校验规则
        return new CredentialMatcher();
    }



    /**
     * 配置我们自己定制的shiro与spring的关联
     * 这样spring在使用shiro进行操作的时候,就是我们自定义的securityManager
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager){

Controller层的书写:

主要还是登录功能,引入了Shiro的核心对象subject的介绍

jsp页面就省略不写了,不过springboot2整合jsp页面教程网上也一堆,等我以后再写吧基本上就是加依赖建包没啥了

@Controller
public class DemoController {

    /**
     * 直接访问login页面
     * @return
     */
    @RequestMapping("/login")
    public String login(){
        return "login";
    }

    /**
     * 登录后的页面
     * @return
     */
    @RequestMapping("/index")
    public String index(){
        return "index";
    }
    /**
     * 初始的登录页面
     */
    @RequestMapping("/loginUser")
    public String loginUser(@RequestParam("username") String username,
                            @RequestParam("password") String password,
                            HttpSession session
    ){
        //根据前端传递过来的name和passowrd生成shrio的UsernamePasswordToken
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        //写shiro的认证逻辑
        Subject subject = SecurityUtils.getSubject();
        //因为可能出现异常,所以直接try catch
        try {
            //调用login方法,传入token
            subject.login(token);
            //如果登录没有出现异常的话,就可以通过getPrincinpal()获取登录用户
            User user= (User)subject.getPrincipal();
            //将登录用户写到session里
            session.setAttribute("user",user);

            //执行成功返回到index页面
            return "index";
        } catch (AuthenticationException e) {
            e.printStackTrace();
            //出错的话返回登录页面
            return "login";
        }

    }

    /**
     * 定义一个退出登录的controller
     * @return
     */
    @RequestMapping("/logout")
    public String logout() {
        //同样是通过实例化subject,进行权限与登录登出的逻辑操作
        //SecurityUtils 是一个抽象类。并且没有任何子类。在其中声明了一个静态属性,三个静态方法。
        //静态属性 securityManager-->用来存储当前应用中全局唯一的一个SecurityManager。
        // getSubject 静态方法-->这个是 Shiro 中最核心的方法了,用来获取 Subject.
        //Subject是Shiro的核心对象,基本所有身份验证、授权都是通过Subject完成
        Subject subject = SecurityUtils.getSubject();
        if (subject != null) {
            subject.logout();
        }
        return "login";
    }

    /**
     * 只有绑定了管理员权限(角色)的用户才能访问admin接口
     * 而绑定是在shiroConfiguration里进行的
     * @return
     */
    @RequestMapping("/admin")
    @ResponseBody
    public String admin() {
        return "admin success";
    }

}

稍微总结一下,如果只看实现的话,我们主要需要运用到shiro的subject对象的各种api,以及shrio核心配置类对不同路径的访问定义不同的权限,至于shrio的延伸,以后应该会更新,另外这里说明一下,这里有好多别人的内容,但是我忘记我学的时候从哪粘贴的了,反正这里先道声抱歉,如果谁看到了可以告诉我,我加一下它的链接

最后补充一点:IDEA中的mavenhelper插件真好用!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值