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文件如下:
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
1.8
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-web
mysql
mysql-connector-java
runtime
org.projectlombok
lombok
true
org.apache.shiro
shiro-spring
1.5.3
com.baomidou
mybatis-plus-boot-starter
3.3.1.tmp
com.github.theborakompanioni
thymeleaf-extras-shiro
2.0.0
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
org.springframework.boot
spring-boot-maven-plugin
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{
}
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 wrapper = new QueryWrapper();
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 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
//登录状态下才可以访问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页面
main
| manage
| admin
index
login页面
| 用户名: | |
| 密码: | |
| 注册 |
register页面
| 用户名: | |
| 密码: | |
main/manage/admin页面
项目源码:
链接:https://pan.baidu.com/s/1EA-664JRpPOpfXaR6L_kkg
提取码:BLU0