SpringBoot 与 Shiro整合

举报
测试-开发阿泽 发表于 2022/12/24 23:03:27 2022/12/24
1.1k+ 0 0
【摘要】 SpringBoot与Shiro整合(认证、授权和密码加密) 创建SpringBoot项目,选择以下工具包: Lombok Spring Web Thyme Leaf MySQL Driver

SpringBoot与Shiro整合(认证、授权和密码加密)


  • 创建SpringBoot项目,选择以下工具包:

    Lombok
    Spring Web
    Thymeleaf
    MySQL Driver

  • 添加MybatisPlus的依赖:

    <dependency>
    	<groupId>com.baomidou</groupId>
    	<artifactId>mybatis-plus-boot-starter</artifactId>
    	<version>3.3.1.tmp</version>
    </dependency>
    
  • 添加Shiro的依赖:

    <dependency>
    	<groupId>org.apache.shiro</groupId>
    	<artifactId>shiro-spring</artifactId>
    	<version>1.5.3</version>
    </dependency>
    
  • 添加Shiro控制ThymeLeaf界面按钮级权限的依赖:

    <dependency>
    	<groupId>com.github.theborakompanioni</groupId>
    	<artifactId>thymeleaf-extras-shiro</artifactId>
    	<version>2.0.0</version>
    </dependency>
    
  • 完整的pom文件如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<parent>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-parent</artifactId>
    		<version>2.3.1.RELEASE</version>
    		<relativePath /> <!-- lookup parent from repository -->
    	</parent>
    	<groupId>com.blu</groupId>
    	<artifactId>springboot-shiro</artifactId>
    	<version>0.0.1-SNAPSHOT</version>
    	<name>springboot-shiro</name>
    	<description>Demo project for Spring Boot</description>
    <properties>
    	<java.version>1.8</java.version>
    </properties>
    
    <dependencies>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-thymeleaf</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-web</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>mysql</groupId>
    		<artifactId>mysql-connector-java</artifactId>
    		<scope>runtime</scope>
    	</dependency>
    	<dependency>
    		<groupId>org.projectlombok</groupId>
    		<artifactId>lombok</artifactId>
    		<optional>true</optional>
    	</dependency>
    	<dependency>
    		<groupId>org.apache.shiro</groupId>
    		<artifactId>shiro-spring</artifactId>
    		<version>1.5.3</version>
    	</dependency>
    	<dependency>
    		<groupId>com.baomidou</groupId>
    		<artifactId>mybatis-plus-boot-starter</artifactId>
    		<version>3.3.1.tmp</version>
    	</dependency>
    	<dependency>
    		<groupId>com.github.theborakompanioni</groupId>
    		<artifactId>thymeleaf-extras-shiro</artifactId>
    		<version>2.0.0</version>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-test</artifactId>
    		<scope>test</scope>
    		<exclusions>
    			<exclusion>
    				<groupId>org.junit.vintage</groupId>
    				<artifactId>junit-vintage-engine</artifactId>
    			</exclusion>
    		</exclusions>
    	</dependency>
    </dependencies>
    <build>
    	<plugins>
    		<plugin>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-maven-plugin</artifactId>
    		</plugin>
    	</plugins>
    </build>
    </project>
    
  • application.yml配置文件:

    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: 123456
        url: jdbc:mysql://localhost:3306/shirotest?useUnicode=true&characterEncoding=UTF-8
        
      thymeleaf:
        prefix: classpath:/templates/
        suffix: .html
        
    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    
  • Account实体类:

    package com.blu.entity;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    
    import lombok.Data;
    
    @Data
    public class Account {
    	@TableId(value = "id",type = IdType.AUTO)
    	private Integer id;
    	private String username;
    	private String password;
    	private String perms;
    	private String role;
    	private String salt;
    }
    
  • 实体类对应数据库:
    在这里插入图片描述

  • AccountMapper接口:

    package com.blu.mapper;
    
    import org.springframework.stereotype.Repository;
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.blu.entity.Account;
    
    @Repository
    public interface AccountMapper extends BaseMapper<Account>{
    
    }
    
  • AccountService接口:

    package com.blu.service;
    
    import com.blu.entity.Account;
    
    public interface AccountService {
    	public Account findByUsername(String username);
    	public void createAccount(Account account);
    
    }
    
  • AccountServiceImpl实现类:

    package com.blu.service.impl;
    
    import org.apache.shiro.crypto.SecureRandomNumberGenerator;
    import org.apache.shiro.crypto.hash.SimpleHash;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.blu.entity.Account;
    import com.blu.mapper.AccountMapper;
    import com.blu.service.AccountService;
    
    @Service
    public class AccountServiceImpl implements AccountService{
    @Autowired
    private AccountMapper mapper;
    
    @Override
    public Account findByUsername(String name) {
    	QueryWrapper<Account> wrapper = new QueryWrapper<Account>();
    	wrapper.eq("username", name);
    	Account account = mapper.selectOne(wrapper);
    	return account;
    }
    
    @Override
    public void createAccount(Account account) {
    	//随机生成salt值,并通过用户注册的密码和salt值经两次md5算法生成真实存储的密码
    	String salt = new SecureRandomNumberGenerator().nextBytes().toString();
    	String password= new SimpleHash("md5",account.getPassword(),salt,2).toString();
    	account.setPassword(password);
    	account.setSalt(salt);
    	mapper.insert(account);
    }
    }
    
  • AccountRealm

    package com.blu.realm;
    
    import java.util.HashSet;
    import java.util.Set;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.crypto.hash.SimpleHash;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.subject.Subject;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import com.blu.entity.Account;
    import com.blu.service.AccountService;
    
    public class AccountRealm extends AuthorizingRealm {
    @Autowired
    private AccountService accountService;
    
    /**
     * 授权
     */
    
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    	//获取当前登录的用户信息
    	Subject subject = SecurityUtils.getSubject();
    	Account account = (Account) subject.getPrincipal();
    	//设置角色
    	Set<String> rolesset = new HashSet<>();
    	rolesset.add(account.getRole());
    	SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(rolesset);
    	//设置权限
    	info.addStringPermission(account.getPerms());
    	return info;
    }
    
    /**
     * 认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    	UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
    	Account account = accountService.findByUsername(token.getUsername());
    	if(account != null){
    		//若密码不正确则返回IncorrectCredentialsException异常
    		return new SimpleAuthenticationInfo(account,account.getPassword(), getName());
    	}
    	//若用户名不存在则返回UnknownAccountException异常
    	return null;
    }
    }
    
  • ShiroConfig配置类:

    package com.blu.config;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import com.blu.realm.AccountRealm;
    
    import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
    
    @Configuration
    public class ShiroConfig {
    	@Bean
    	public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
    		ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
    		factoryBean.setSecurityManager(securityManager);
    		Map<String,String> map = new HashMap<String, String>();
    		//登录状态下才可以访问main页面,manage权限可访问manage页面,admin角色可访问admin页面
    		map.put("/main", "authc");
    		map.put("/manage","perms[manage]");
    		map.put("/admin", "roles[admin]");
    		factoryBean.setFilterChainDefinitionMap(map);
    		//未登录状态下访问将跳转至login页面
    		factoryBean.setLoginUrl("/login");
    		//无授限状态下访问将请求unauthor
    		factoryBean.setUnauthorizedUrl("/unauthor");
    		return factoryBean;
    	}
    	@Bean
    	public DefaultWebSecurityManager securityManager(@Qualifier("accoutRealm") AccountRealm accountRealm){
    		DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
    		manager.setRealm(accountRealm);
    		return manager;
    	}
    	
    	@Bean
    	public AccountRealm accoutRealm(){
    		return new AccountRealm();
    	}
    	@Bean
    	public ShiroDialect shiroDialect(){
    		return new ShiroDialect();
    	}
    
    }
    
  • AccountController:

    package com.blu.controller;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.IncorrectCredentialsException;
    import org.apache.shiro.authc.UnknownAccountException;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.crypto.hash.SimpleHash;
    import org.apache.shiro.subject.Subject;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import com.blu.entity.Account;
    import com.blu.service.AccountService;
    
    @Controller
    public class AccountController {
    	@Autowired
    	private AccountService acccoutService;
    	
    	@GetMapping("/{url}")
    	public String redirect(@PathVariable("url") String url) {
    		return url;
    	}
    	
    	@PostMapping("/login")
    	public String login(String username,String password,Model model) {
    		Subject subject = SecurityUtils.getSubject();
    		Account ac = acccoutService.findByUsername(username);
    		if(ac!=null) {
    			//根据salt值和用户输入的密码计算加密后的密码
    			String salt = ac.getSalt();
    			password = new SimpleHash("md5",password,salt,2).toString();
    		}
    		UsernamePasswordToken token = new UsernamePasswordToken(username,password);
    		try {
    			//将用户名和密码通过token传给shiro进行认证
    			subject.login(token);
    			Account account = (Account) subject.getPrincipal();
    			subject.getSession().setAttribute("account", account);
    			return "index";
    		} catch (UnknownAccountException e) {
    			e.printStackTrace();
    			model.addAttribute("msg", "用户名不存在");
    			return "login";
    		} catch (IncorrectCredentialsException e) {
    			e.printStackTrace();
    			model.addAttribute("msg", "密码有误");
    			return "login";
    		}
    		
    	}
    	
    	@ResponseBody
    	@GetMapping("/unauthor")
    	public String unauthor() {
    		return "权限不足,无法访问";
    	}
    	
    	@GetMapping("/logout")
    	public String logout() {
    		Subject subject = SecurityUtils.getSubject();
    		subject.logout();
    		return "login";
    	}
    	
    	@PostMapping("/register")
    	public String register(Account account,Model model) {
    		String username = account.getUsername();
    		String password = account.getPassword();
    		if(username==null||username==""){
    			model.addAttribute("msg", "用户名不能为空");
    			return "register";
    		}else if(password==null||password=="") {
    			model.addAttribute("msg", "密码不能为空");
    			return "register";
    		}else if(acccoutService.findByUsername(username)!=null) {
    			model.addAttribute("msg", "用户名已被占用");
    			return "register";
    		}else {
    			acccoutService.createAccount(account);
    			return "login";
    		}
    	}
    
    }
    
  • ShiroApplication启动类:

    package com.blu;
    
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    @MapperScan("com.blu.mapper")
    public class ShiroApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(ShiroApplication.class, args);
    		
    	}
    
    }
    

    index页面

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.thymrleaf.org/thymeleaf-extras-shiro">
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <link rel="shortcut icon" href="#"/> 
    </head>
    <body>
    	<div th:if="${session.account != null}">
    		<span th:text="'欢迎回来 '+${session.account.username}+'!  '">
    		</span><a href="/logout">退出</a> 
    	</div>
    <a href="/main">main</a> 
    <span shiro:hasPermission="manage"> | <a href="/manage">manage</a></span>
    <span shiro:hasRole="admin"> | <a href="/admin">admin</a></span>
    
    <br>
    <h1>index</h1>
    </body>
    </html>
    
  • login页面

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org">
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <link rel="shortcut icon" href="#"/> 
    </head>
    <body>
    	<form action="/login" method="post">
    		<span th:text="${msg}" style="color: red"></span>
    		<table>
    			<tr>
    				<td>用户名:</td>
    				<td><input type="text" name="username"/></td>
    			</tr>
    			<tr>
    				<td>密码:</td>
    				<td><input type="password" name="password"/></td>
    			</tr>
    			<tr>
    				<td><input type="submit" value="登录"/></td>
    				<td><a href="/register">
    						<button type="button" value="注册">注册</button>
    					</a>
    				</td>
    			</tr>
    		</table>
    	</form>
    </body>
    </html>
    

image.png

  • register页面

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org">
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <link rel="shortcut icon" href="#"/>
    </head>
    <body>
    	<form action="/register" method="post">
    	<span th:text="${msg}" style="color: red"></span>
    		<table>
    			<tr>
    				<td>用户名:</td>
    				<td><input type="text" name="username"/></td>
    			</tr>
    			<tr>
    				<td>密码:</td>
    				<td><input type="password" name="password"/></td>
    			</tr>
    			<tr>
    				<td><input type="submit" value="注册"/></td>
    			</tr>
    		</table>
    	</form>
    </body>
    </html>
    

image.png

  • main/manage/admin页面
    main

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <link rel="shortcut icon" href="#"/> 
    </head>
    <body>
    	<h1>main</h1>
    </body>
    </html>
    

    manage

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <link rel="shortcut icon" href="#"/> 
    </head>
    <body>
    	<h1>manage</h1>
    </body>
    </html>
    

    admin

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <link rel="shortcut icon" href="#"/> 
    </head>
    <body>
    	<h1>admin</h1>
    </body>
    </html>
    
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

作者其他文章

评论(0

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。