SpringBoot整合shiro

第一步:导入依赖:

整合shiro框架
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>
页面使用Shiro标签
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>
引用Thymeleaf
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

第二步:创建Realm文件

@Component
public class CustomRealm extends AuthorizingRealm {

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        System.out.println("[FirstRealm] doGetAuthenticationInfo");

        //1. 把 AuthenticationToken 转换为 UsernamePasswordToken
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;

        //2. 从 UsernamePasswordToken 中来获取 username
        String username = upToken.getUsername();

        //3. 调用数据库的方法, 从数据库中查询 username 对应的用户记录
        System.out.println("从数据库中获取 username: " + username + " 所对应的用户信息.");

        //4. 若用户不存在, 则可以抛出 UnknownAccountException 异常
        if("unknown".equals(username)){
            throw new UnknownAccountException("用户不存在!");
        }

        //5. 根据用户信息的情况, 决定是否需要抛出其他的 AuthenticationException 异常.
        if("monster".equals(username)){
            throw new LockedAccountException("用户被锁定");
        }

        //6. 根据用户的情况, 来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为: SimpleAuthenticationInfo
        //以下信息是从数据库中获取的.
        //1). principal: 认证的实体信息. 可以是 username, 也可以是数据表对应的用户的实体类对象.
        Object principal = username;
        //2). credentials: 密码.
        Object credentials = null; //"fc1709d0a95a6be30bc5926fdb7f22f4";
        if("admin".equals(username)){
            credentials = "038bdaf98f2037b31f1e75b5b4c9b26e";
        }else if("user".equals(username)){
            credentials = "098d2c478e9c11555ce2823231e02ec1";
        }

        //3). realmName: 当前 realm 对象的 name. 调用父类的 getName() 方法即可
        String realmName = getName();
        //4). 盐值.
        ByteSource credentialsSalt = ByteSource.Util.bytes(username);

        SimpleAuthenticationInfo info = null; //new SimpleAuthenticationInfo(principal, credentials, realmName);
        info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
        return info;
    }


    //授权会被 shiro 回调的方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principals) {
        //1. 从 PrincipalCollection 中来获取登录用户的信息
        Object principal = principals.getPrimaryPrincipal();

        //2. 利用登录的用户的信息来用户当前用户的角色或权限(可能需要查询数据库)
        Set<String> roles = new HashSet<>();
        roles.add("user");
        if("admin".equals(principal)){
            roles.add("admin");
        }

        //3. 创建 SimpleAuthorizationInfo, 并设置其 reles 属性.
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);

        //4. 返回 SimpleAuthorizationInfo 对象.
        return info;
    }
}

第三步:创建ShiroConfig文件 等同于 在spring中的applicationContext.xml
注意:包名别导入错误

import com.boot.shiro.bootshiro.realms.CustomRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.*;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @author GuoLeiCode
 * @version 1.0
 * @date 2020/10/5 19:25
 */
@Configuration
public class ShiroConfig {
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl("/shiro/login.html");
        shiroFilterFactoryBean.setSuccessUrl("/shiro/list.html");
        shiroFilterFactoryBean.setUnauthorizedUrl("/shiro/notRole");
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/shiro/login.html", "anon");
        filterChainDefinitionMap.put("/shiro/doLogin", "anon");
        filterChainDefinitionMap.put("/shiro/logout", "logout");
        filterChainDefinitionMap.put("/shiro/user.html", "authc,roles[user]");
        filterChainDefinitionMap.put("/shiro/admin.html", "authc,roles[admin]");
        filterChainDefinitionMap.put("/shiro/list.html", "user");
        //主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截 剩余的都需要认证
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
    @Bean("securityManager")
    public SecurityManager securityManager() {
        DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
        //第一种:一个Realm文件
        defaultSecurityManager.setRealm(customRealm());  //只用一个Realm文件

        //第二种: 多个Realm文件,使用下面两行的代码

        //defaultSecurityManager.setRealms(realmList()); // 使用多个Realm
        //defaultSecurityManager.setAuthenticator(authenticator()); // 多个Realm -> 选择认证的的方式

        return defaultSecurityManager;
    }

    //设置单个Realm,也可以使用 @Component 注册到spring容器中,不使用new的操作
    @Bean
    public CustomRealm customRealm() {
        CustomRealm customRealm = new CustomRealm();
      
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("MD5"); //还可以是SHA1
        hashedCredentialsMatcher.setHashIterations(1024); // 加密次数
        hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
        customRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        return customRealm;
    }

    /**
     *在SecurityManager中设置多个Realm时,使用下面的注释
     */
//    //设置多个Realm
//    @Bean
//    public List<Realm> realmList(){
//        List<Realm> realms = new ArrayList<>();
//        CustomRealm customRealm = new CustomRealm();
//        realms.add(customRealm);
//        return  realms;
//    }
//    //设置多个Realm的认证方式
//    @Bean
//    public ModularRealmAuthenticator authenticator(){
//        ModularRealmAuthenticator modularRealmAuthenticator = new ModularRealmAuthenticator();
        AuthenticationStrategy接口默认实现:
        ① FirstSuccessfulStrategy: 只要有一个Realm验证成功即可,只返回第一个Realm身份验证成功的认证信息,其他忽略
        ② AtLeastOneSuccessfulStrategy:只要有一个Realm验证成功,将返回所有Realm身份验证成功的认证信息
        ③ AllSuccessfulStrategy:所有Realm验证成功才算成功,且返回所有Realm身份验证成功的认证信息,一个失败就失败了
        ModularRealmAuthenticator默认是第②种
//        modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
//        modularRealmAuthenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());
//        modularRealmAuthenticator.setAuthenticationStrategy(new AllSuccessfulStrategy());
//        return  modularRealmAuthenticator;
//    }

    /**
     * 2、配置 LifecycleBeanPostProcessor. 可以自定的来调用配置在 Spring IOC 容器中 shiro bean 的生命周期方法.
     */
    @Bean("lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 3、开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     *
     * 配置以下两个bean(
     * DefaultAdvisorAutoProxyCreator(可选)、
     * AuthorizationAttributeSourceAdvisor即可实现此功能
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

}

applicationContext.xml 和 代码 对照关系

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第四步:控制器和页面的编写进行测试

@Controller
@RequestMapping("/shiro")
public class ShiroController {
    @RequestMapping(path = "/login.html")
    public String toLogin(){
        return "login";
    }
    @RequestMapping("/list.html")
    public String toList(){
        return "list";
    }
    @RequestMapping("/user.html")
    public String toUser(){
        return "user";
    }
    @RequestMapping({"/unauthorized","/unauthorized.html"})
    public String toUnauthorized(){
        return "unauthorized";
    }
    @RequestMapping(path = {"/admin.html"})
    public String toAdmin(){
        return "admin";
    }
    @ResponseBody
    @RequestMapping("notRole")
    public String noRole(){return "not Role";}




    @RequestMapping("/doLogin")
    public String doLogin(@RequestParam("username") String username,
                          @RequestParam("password") String password){
        Subject currentUser = SecurityUtils.getSubject();

        if (!currentUser.isAuthenticated()) {
            // 把用户名和密码封装为 UsernamePasswordToken 对象
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            // rememberme
            token.setRememberMe(true);
            try {
                System.out.println("1. " + token.hashCode());
                // 执行登录.
                currentUser.login(token);
            }
            // ... catch more exceptions here (maybe custom ones specific to your application?
            // 所有认证时异常的父类.
            catch (AuthenticationException ae) {
                //unexpected condition?  error?
                System.out.println("登录失败: " + ae.getMessage());
            }
        }
        return "redirect:/shiro/list.html";
    }
}

页面创建,每个页面有自己独特的标识即可
在这里插入图片描述
示例list.html ;使用了shiro标签:需要提前引入对应的标签

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <meta charset="UTF-8">
    <title>list页面</title>
</head>
<body>
    <h4>List Page</h4>

    Welcome: <shiro:principal></shiro:principal>

    <shiro:hasRole name="admin">
        <br><br>
        <a href="/shiro/admin.html">Admin Page</a>
    </shiro:hasRole>

    <shiro:hasRole name="user">
        <br><br>
        <a href="/shiro/user.html">User Page</a>
    </shiro:hasRole>


    <br><br>
    <a href="/shiro/logout">Logout</a></body>
</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值