SpringBoot与Shiro整合(认证、授权和密码加密)
-
创建SpringBoot项目,选择以下工具包:
Lombok
Spring Web
Thymeleaf
MySQL Driver -
添加MybatisPlus的依赖:
com.baomidou mybatis-plus-boot-starter 3.3.1.tmp -
添加Shiro的依赖:
org.apache.shiro shiro-spring 1.5.3 -
添加Shiro控制ThymeLeaf界面按钮级权限的依赖:
com.github.theborakompanioni thymeleaf-extras-shiro 2.0.0 -
完整的pom文件如下:
<?xml version="1.0" encoding="UTF-8"?>
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.3.1.RELEASE
com.blu
springboot-shiro
0.0.1-SNAPSHOT
springboot-shiro
Demo project for Spring Boot<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>
-
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-8thymeleaf:
prefix: classpath:/templates/
suffix: .htmlmybatis-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{}
-
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页面
Insert title here <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>
-
login页面
Insert title here 用户名: 密码: 注册 -
register页面
Insert title here 用户名: 密码: -
main/manage/admin页面
Insert title here main
Insert title here manage
Insert title here admin
项目源码:
链接:https://pan.baidu.com/s/1EA-664JRpPOpfXaR6L_kkg
提取码:BLU0