SpringBoot 与 Shiro整合
【摘要】 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>
-
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>
-
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)