SpringToolS创建SpringBoot+Shiro项目 - 寒江孤影

SpringToolS创建SpringBoot+Shiro项目

作者: 36D小可爱

全网最全的网络资源分享网站

手机扫码查看

标签:

SpringBootShiro

特别声明:文章多为网络转载,资源使用一般不提供任何帮助,特殊资源除外,如有侵权请联系!

教程演示环境

SpringToolS uite4中文汉化版apache-maven-3.6.3jdk1.8.0_191, Shiro1.7.1

本教程需要先配置Maven才能创建项目,可参考 SpringToolS配置Maven

第一步创建

1.首先点击 文件=>新建=>Spring Starter Project ,如果没有 Spring Starter Project 就点击其他搜索一下

QQ截图20210319103100.png

2.配置 Service URL 选择自定义 采用阿里云镜像 https://start.aliyun.com 写项目名然后点击下一步

QQ截图20210320014958.png

3.依赖选择 Spring Web 选择下一步,完成

QQ截图20210320015222.png

第二步测试

1.本地测试,双击打开 SpringbootApplication 右键运行方式选择SpringBootApp

或者左下角的 local 点击springboot点击运行按钮,控制台打印出 Spring 运行成功

QQ截图20210320015956.png

2.打开pom.xml文件在 <dependencies> 标签下加入Shiro依赖

<!-- shiro -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.7.1</version>
</dependency>

第三步配置Shiro

创建config配置包

1.创建ShiroConfig.java类,过滤的文件和权限,密码加密的算法,其用注解等相关功能

package com.example.demo.config;

import org.apache.shiro.mgt.SecurityManager;
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.apache.shiro.web.session.mgt.DefaultWebSessionManager;
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.LinkedHashMap;
import java.util.Map;

/**
 * 自定义Shiro配置
 * 
 * @author 36D小可爱
 * @since 2021-04-28
 * @Description 过滤的文件和权限,密码加密的算法,其用注解等相关功能
 */
@Configuration
public class ShiroConfig {
	
	// Shiro过滤器配置
	@Bean(name = "shiroFilter")
	public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
		System.out.println("-------Shiro过滤器开始--------");
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		// Shiro的核心安全接口
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		// 拦截器定义
		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
		// 登录配置
		shiroFilterFactoryBean.setLoginUrl("/login");// 登录界面跳转页面,不配置则跳转至“/”
		//setUnauthorizedUrl只有perms,roles,ssl,rest,port支持跳转
		shiroFilterFactoryBean.setUnauthorizedUrl("/error");// 未授权跳转界面
		/**
		 * “/”匹配单url “*” 匹配一级url “**” 匹配所有url
		 */
		// authc:表示需要认证(登录)才能使用 anon:表示可以匿名使用
		filterChainDefinitionMap.put("/test/**", "anon");// 不会被拦截的链接要写在 “/**” 之前 顺序判断
		filterChainDefinitionMap.put("/logout", "logout");// 退出登录后跳转至“/”
		filterChainDefinitionMap.put("/", "anon");// 允许首页访问
		// 必须放在所有权限设置的最后,不然会导致所有 url 都被拦截 剩余的都需要认证
		filterChainDefinitionMap.put("/**", "authc");
		// 登录成功后要跳转的链接
		shiroFilterFactoryBean.setSuccessUrl("/index");
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
		return shiroFilterFactoryBean;

	}

	// 安全管理器权限管理,配置主要是Realm的管理认证
	@Bean(name = "securityManager")
	public SecurityManager securityManager() {
		DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
		System.out.println("-------注入认证和授权具体实现类--------");
		// 注入自定义Realm管理认证
		defaultSecurityManager.setRealm(customRealm());
		// 注入session的管理
		defaultSecurityManager.setSessionManager(sessionManager());
		return defaultSecurityManager;
	}

	// 用户登录认证时自定义realm管理认证
	@Bean(name = "customRealm")
	public CustomRealm customRealm() {
		// 认证和授权具体实现类
		CustomRealm customRealm = new CustomRealm();
        // 注入加密算法类来验证密文(使用Shiro的md5验证方式可解开当前注释)
		//customRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        customRealm.setCachingEnabled(false);
		return customRealm;
	}

	//session的管理器
	@Bean(name = "sessionManager")
	public DefaultWebSessionManager sessionManager() {
		DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
		// shiro重定向url去除JSSESSIONID
		sessionManager.setSessionIdUrlRewritingEnabled(false);
		return sessionManager;
	}

	@Bean(name = "lifecycleBeanPostProcessor")
	public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
		LifecycleBeanPostProcessor lifecycleBeanPostProcessor = new LifecycleBeanPostProcessor();
		return lifecycleBeanPostProcessor;
	}

	// 拦截注解的方法
	@Bean(name = "authorizationAttributeSourceAdvisor")
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
		AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
		advisor.setSecurityManager(securityManager());
		return advisor;
	}

	@Bean
	@DependsOn({ "lifecycleBeanPostProcessor" })
	public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
		advisorAutoProxyCreator.setProxyTargetClass(true);
		return advisorAutoProxyCreator;
	}
	/*
	* Shiro密码加密(使用Shiro的md5验证方式可解开当前注释)
	@Bean(name = "credentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        // 散列的次数,比如散列两次,相当于 md5(md5(""));
        hashedCredentialsMatcher.setHashIterations(2);
        // storedCredentialsHexEncoded默认是true,此时用的是密码加密用的是Hex编码;false时用Base64编码
        hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
        return hashedCredentialsMatcher;
    }
    */
	
}

2.创建CustomRealm.java类,自定义Realm管理认证,用于查询用户的角色和权限信息并保存到权限管理器

package com.example.demo.config;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import com.example.demo.util.MD5Util;

import java.util.HashSet;
import java.util.Set;

/**
 * 自定义Realm管理认证
 * 
 * @author 36D小可爱
 * @since 2021-04-28
 * @Description 用于查询用户的角色和权限信息并保存到权限管理器
 */
public class CustomRealm extends AuthorizingRealm {

	/**
	 * 权限配置类 授权管理,不设置可以返回null
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
		System.out.println("-------权限认证开始--------");

		// 获取令牌用户名
		String userName = (String) SecurityUtils.getSubject().getPrincipal();
		// 角色和权限对象
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
		// 设置角色标识
		Set<String> roles = new HashSet<>();
		// 角色可以根据userName从数据库获取角色id 可以用String.valueOf(1)
		roles.add("1");
		roles.add("2");
		roles.add("3");

		authorizationInfo.setRoles(roles);// 角色

		// 设置权限标识
		Set<String> permissions = new HashSet<>();
		// 权限标识可以根据userName从数据库获取
		permissions.add("user:view");
		permissions.add("user:add");
		permissions.add("user:edit");
		permissions.add("user:del");

		authorizationInfo.setStringPermissions(permissions);// 权限
		return authorizationInfo;
	}

	/**
	 * 认证配置类
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
		System.out.println("-------身份认证开始--------");
		// 获取令牌用户名
		String userName = (String) authenticationToken.getPrincipal();
		// 获取令牌密码
		String userPwd = new String((char[]) authenticationToken.getCredentials());
		// 获取数据库的密码
		String password = "c53d50743d34f39c37697e0e25a22a11c38161a94f52b320";// 123456
		if (!MD5Util.verify(userPwd, password)) {
			throw new IncorrectCredentialsException("密码错误");
		}
		
		// throw new LockedAccountException("账户已锁定");
		// throw new ExcessiveAttemptsException("用户名或密码错误次数过多");//密码错误次数过多
		
		// 密码验证(第三个参数传入md5的盐,自定义密码验证忽略参数)
		return new SimpleAuthenticationInfo(userName, userPwd, getName());
	}

}

3.创建NoPermissionException.java类,对Shiro没有权限访问接口抛出的异常处理

package com.example.demo.config;

import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * Shiro没有权限时处理
 * 
 * @author 36D小可爱
 * @since 2021-04-28
 * @Description 对Shiro没有权限访问接口抛出的异常处理
 */
@ControllerAdvice
public class NoPermissionException {
	
    @ResponseBody
    @ExceptionHandler(UnauthorizedException.class)
    public String handleShiroException(Exception ex) {
        return "无权限";
    }
    
    @ResponseBody
    @ExceptionHandler(AuthorizationException.class)
    public String AuthorizationException(Exception ex) {
        return "权限认证失败";
    }
    
}

创建controller包

1.创建一个TestController.java,测试登录认证

package com.example.demo.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;


/**
 * 测试登录认证
 * 
 * @author 36D小可爱
 * @since 2021-04-28
 */
@Controller
public class TestController {
	
    @RequestMapping("/login")
    @ResponseBody
    public String login(@RequestParam(value="username", required=true) String username,@RequestParam(value="password", required=true) String password) {
    	// 从SecurityUtils里边创建一个 subject
        Subject subject = SecurityUtils.getSubject();
        // 在认证提交前创建 token(令牌)
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
        	// 执行认证登陆
            subject.login(token);
        } catch (UnknownAccountException uae) {
            return "未知账户";
        } catch (IncorrectCredentialsException ice) {
            return "密码不正确";
        } catch (LockedAccountException lae) {
            return "账户已锁定";
        } catch (ExcessiveAttemptsException eae) {
            return "用户名或密码错误次数过多";
        } catch (AuthenticationException ae) {
            return "用户名或密码不正确!";
        }
        //用户是否已通过身份验证
        if (subject.isAuthenticated()) {
            return "登录成功";
        } else {
            token.clear();
            return "登录失败";
        }
    }
	
	@RequestMapping("/admin")
    @ResponseBody
    public String admin() {
		return "管理员后台界面";
    }
	
	@RequestMapping("/")
    @ResponseBody
    public String index() {
		return "前台界面";
    }
	
	@RequestMapping("/test")
    @ResponseBody
    public String test() {
		return "认证成功界面";
    }
}

2.创建UserControllet.java,测试权限

package com.example.demo.controller;

import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * 测试权限
 * 
 * @author 36D小可爱
 * @since 2021-04-28
 */
@RequiresRoles("1")//角色标识
@Controller
public class UserControllet {
	
	@RequiresPermissions("user:view")//权限标识
    @ResponseBody
    @RequestMapping("/userList")
    public String showUser() {
        return "用户信息";
    }
	
}

创建util工具类包

1.创建MD5Util.java类

package com.example.demo.util;

import org.apache.shiro.codec.Hex;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;

public class MD5Util {

    /**
     * 普通MD5加密<br>
     *
     * @param input
     * @return
     */
    public static String MD5(String input) {
        MessageDigest md5 = null;
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            return "check jdk";
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
        char[] charArray = input.toCharArray();
        byte[] byteArray = new byte[charArray.length];

        for (int i = 0; i < charArray.length; i++)
            byteArray[i] = (byte) charArray[i];
        byte[] md5Bytes = md5.digest(byteArray);
        StringBuffer hexValue = new StringBuffer();
        for (int i = 0; i < md5Bytes.length; i++) {
            int val = ((int) md5Bytes[i]) & 0xff;
            if (val < 16)
                hexValue.append("0");
            hexValue.append(Integer.toHexString(val));
        }
        return hexValue.toString();

    }

    /**
     * 加盐MD5加密<br>
     *
     * @param password
     * @return
     */
    public static String getMD5(String password) {
        Random r = new Random();
        StringBuilder sb = new StringBuilder(16);
        sb.append(r.nextInt(99999999)).append(r.nextInt(99999999));
        int len = sb.length();
        if (len < 16) {
            for (int i = 0; i < 16 - len; i++) {
                sb.append("0");
            }
        }
        String salt = sb.toString();
        password = md5Hex(password + salt);
        char[] cs = new char[48];
        for (int i = 0; i < 48; i += 3) {
            cs[i] = password.charAt(i / 3 * 2);
            char c = salt.charAt(i / 3);
            cs[i + 1] = c;
            cs[i + 2] = password.charAt(i / 3 * 2 + 1);
        }
        return new String(cs);
    }

    /**
     * 校验加盐后是否和原文一致<br>
     *
     * @param password
     * @param md5
     * @return
     */
    public static boolean verify(String password, String md5) {
        try {
            char[] cs1 = new char[32];
            char[] cs2 = new char[16];
            for (int i = 0; i < 48; i += 3) {
                cs1[i / 3 * 2] = md5.charAt(i);
                cs1[i / 3 * 2 + 1] = md5.charAt(i + 2);
                cs2[i / 3] = md5.charAt(i + 1);
            }
            String salt = new String(cs2);
            return md5Hex(password + salt).equals(new String(cs1));
        } catch (Exception e) {
            return false;
        }

    }

    /**
     * 获取十六进制字符串形式的MD5摘要
     */
    private static String md5Hex(String src) {
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            byte[] bs = md5.digest(src.getBytes());
            return new String(new Hex().encode(bs));
        } catch (Exception e) {
            return null;
        }
    }

}

第三步启动项目测试Shiro

测试Shiro登录认证

1.在ShiroConfig.java中开放了首页访问权限

QQ截图20210425103153.png

2.在没有登录认证时无法访问其他接口,只能访问没有被拦截的接口,访问其他接口则会重定向到登录接口

访问 http://localhost:8080/admin 会自动跳转到 http://localhost:8080/login

QQ截图20210429090402.png

3.登录测试,本演示过程中采用GET请求方式,正式环境请用POST请求方式

访问 http://localhost:8080/login?username=admin&password=123456 即可登陆成功

QQ截图20210429091004.png

4.再次访问之前的 http://localhost:8080/admin 访问成功

QQ截图20210429091114.png

5.访问 http://localhost:8080/logout 即可退出登录,退出登录后会自动跳转到首页

QQ截图20210425103153.png

测试Shiro角色权限认证

角色测试

1.首先去掉 CustomRealm.java中的角色和权限,重启项目

QQ截图20210429092203.png

2.首先是登录,访问 http://localhost:8080/login?username=admin&password=123456 即可登陆成功。

登录成功后做角色权限测试,访问 http://localhost:8080/userList 会显示无权限

QQ截图20210429092331.png

3.只解开角色的注释

QQ截图20210429092526.png

4.在 UserControllet.java 再写一个测试接口代码如下

@ResponseBody
@RequestMapping("/useradd")
public String addUser() {
    return "用户信息添加";
}

QQ截图20210429093047.png

5. 重启项目 访问 http://localhost:8080/login?username=admin&password=123456 即可登陆。

登录成功后只有角色没有权限,访问不带权限的 http://localhost:8080/useradd 显示用户信息添加。

QQ截图20210429093327.png

访问带权限的 http://localhost:8080/userList 则没有权限

QQ截图20210429092331.png

权限测试

6.解开权限的注释 重启项目 访问 http://localhost:8080/login?username=admin&password=123456 即可登陆。

登录成功后有角色和权限,访问带角色和权限的 http://localhost:8080/userList 显示用户信息。

QQ截图20210429094054.png

该附件已被管理员隐藏 您需要 回复 后并刷新才可以下载
分享到:
打赏
-版权声明-

作者: 36D小可爱, 转载或复制请以 超链接形式 并注明出处 寒江孤影
原文地址: 《SpringToolS创建SpringBoot+Shiro项目》 发布于2021-4-29

阅读时间:   发布于:2021-4-29
文章标题:《SpringToolS创建SpringBoot+Shiro项目》
本文链接:https://a14.cn:443/post-25.html
本文编辑: 36D小可爱,转载请注明超链接和出处寒江孤影
收录状态:[百度已收录][360已收录][搜狗已收录]

评论一下

切换注册

登录

忘记密码?

您也可以使用第三方帐号快捷登录

切换登录

注册

SpringToolS创建SpringBoot+Shiro项目

长按图片转发给朋友

觉得文章有用就打赏一下文章作者

花呗扫一扫打赏

微信扫一扫打赏

企鹅扫一扫打赏

召唤伊斯特瓦尔