springboot整合shiro
使用 springboot整合shiro其实和使用 SSM整合shiro很相似,只不过使用springboot整合shiro中间简化了很多文件配置,但是主体内容基本一致
1.springboot整合shiro
1.1项目结构图
1.2访问流程
1.3项目内容
1.3.1配置类及pom文件
- 项目启动类
@SpringBootApplication
@MapperScan("com.fyx.dao")//包扫描
public class Day0408SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(Day0408SpringbootApplication.class, args);
}
}
- ①首先创建springboot项目,在pom.xml文件中导入相关依赖。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.14</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.4.1</version>
</dependency>
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>5.1.47</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
- ②application.properties
# 配置数据源
spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.druid.url=jdbc:mysql:///shiro
spring.datasource.druid.username=root
spring.datasource.druid.password=123@qwe
- shiro的配置文件
在ssm项目中,shiro的组件创建在spring配置文件中,在springboot项目中,需要自己创建一个配置类
ShiroConfig类
@Configuration//等价于spring的配置文件
public class ShiroConfig {
// 配置安全管理器
@Bean("securityManager")
public DefaultWebSecurityManager securityManager(Realm myRealm){
DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
securityManager.setRealm(myRealm);
return securityManager;
}
// 创建MyRealm对象
@Bean(value = "myRealm")//依赖注入
public Realm getRealm(CredentialsMatcher credentialsMatcher){
MyRealm myRealm = new MyRealm();
myRealm.setCredentialsMatcher(credentialsMatcher);
return myRealm;
}
@Bean(value = "credentialsMatcher")//密码配置器
public CredentialsMatcher getCredentialsMatcher(){
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashIterations(1024);//散列次数
credentialsMatcher.setHashAlgorithmName("MD5");//使用MD5加密
return credentialsMatcher;
}
// 配置过滤规则
@Bean("shiroFilter")//shiro过滤
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("/tologin");//路径
shiroFilterFactoryBean.setSuccessUrl("/success.html");//访问成功后调转的路径
HashMap<String, String> map = new HashMap<>();
map.put("/index.html","anon");//
map.put("/static/**","anon");
map.put("/login","anon");
map.put("/**","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
// 配置过滤器 相当于ssm项目中web.xml的给shiro配置过滤器
@Bean
public FilterRegistrationBean filterRegistrationBean(){
FilterRegistrationBean filterRegistrationBean=new FilterRegistrationBean();
filterRegistrationBean.setName("shiroFilter");
filterRegistrationBean.setFilter(new DelegatingFilterProxy());
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
//使shiro标签库可以在thymeleaf中使用
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
}
1.3.2html文件
springboot项目不支持jsp,只能使用html,要使用thymeleaf模板,需要导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
- login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/login" method="post">
账号:<input type="text" name="username"/><br>
密码:<input type="text" name="userpwd"/><br>
<input type="submit" value="登陆"/>
<input type="button" value="注册" onclick="location.href='regist'"/>
</form>
</body>
</html>
- success.html
在这个页面中需要使用shiro的标签,要在页面中引入shiro的标签,也要在pom.xml中引入依赖
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<!DOCTYPE html>
<html lang="en" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8">
<title>成功页面</title>
</head>
<body>
成功 <h1>欢迎来到xxx班 尽情欢呼!</h1>
<shiro:hasPermission name="user:query">
<a href="/user/query">查询所有用户</a><br>
</shiro:hasPermission>
<shiro:hasPermission name="user:update">
<a href="/user/update">修改用户</a><br>
</shiro:hasPermission>
<shiro:hasPermission name="user:delete">
<a href="/user/delete">删除用户</a><br>
</shiro:hasPermission>
<shiro:hasPermission name="user:insert">
<a href="/user/insert">添加用户</a><br>
</shiro:hasPermission>
<shiro:hasPermission name="user:export">
<a href="/user/export">导出用户</a><br>
</shiro:hasPermission>
</body>
</html>
- un.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
无权访问
</body>
</html>
1.3.3后端类文件
- dao层
①UserDao
public interface UserDao extends BaseMapper<User> {
/**
* 根据用户id查询该用户具有的权限
* @param userid
* @return
*/
List<String> findPermissionByUserid(Integer userid);
}
②PermissionDao
public interface PermissionDao {
/**
* 根据userid查询用户权限
*/
List<String> findAllPermissionByUserId(Integer userid);
}
- entity层
User
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer userid;
private String username;
private String userpwd;
private String sex;
private String address;
private String salt;//盐
}
- service层
①UserService
public interface UserService {
User selectByUsername(String username);
}
②UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserDao userDao;
@Override
public User selectByUsername(String username) {
//条件构造器
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("username",username);//相当于username=#{username}
User user = userDao.selectOne(wrapper);
return user;
}
}
- user的映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fyx.dao.UserDao">
<select id="findPermissionByUserid" resultType="String">
select percode from user_role ur,role_permission rp, permission p
where ur.roleid=rp.roleid and rp.perid=p.perid and ur.userid=#{userid}
</select>
</mapper>
- Controller层
①PageController
@Controller
public class PageController {
@GetMapping("tologin")
public String tologin(){
return "login";
}
}
②LoginController
@Controller
public class LoginController {
@PostMapping("login")
public String login(String username,String userpwd){
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username,userpwd);
try{
subject.login(token);//开始认证
return "success";
}catch (Exception e){
return "redirect:/tologin";
}
}
}
③UserController
@RestController
@RequestMapping("user")
public class UserController {
@GetMapping("query")
@RequiresPermissions("user:query")//当前Subject需要拥有某些特定的权限时,才能执行被该注解标注的方法。如果当前Subject不具有这样的权限,则方法不会被执行。
public String query(){
return "user:query";
}
@GetMapping("delete")
@RequiresPermissions("user:delete")
public String delete(){
return "user:delete";
}
@GetMapping("update")
@RequiresPermissions("user:update")
public String update(){
return "user:update";
}
@GetMapping("insert")
@RequiresPermissions("user:insert")
public String insert(){
return "user:insert";
}
@GetMapping("export")
@RequiresPermissions("user:export") //@Transcational
public String export(){
return "user:export";
}
}
- handler层
ExceptionController : 处理异常
@ControllerAdvice
public class ExceptionController {
@ExceptionHandler(value = AuthorizationException.class)
public String handler(){
return "un";
}
}
- realm层
MyRealm类
public class MyRealm extends AuthorizingRealm {
@Resource
private UserService userService;
@Resource
private PermissionService permissionService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//查询用户权限信息
User user = (User)principalCollection.getPrimaryPrincipal();
//通过userid查询对应用户所具有的权限
List<String> userId = permissionService.findAllPermissionByUserId(user.getUserid());
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//判断查询的权限集合是否有值
if (userId.size()>0){
//将权限添加到授权对象中
info.addStringPermissions(userId);
}
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取前台传的用户名
String username = authenticationToken.getPrincipal().toString();
User user = userService.selectByUsername(username);
if (user!=null){
ByteSource bytes = ByteSource.Util.bytes(user.getSalt());
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getUserpwd(),bytes,this.getName());
return info;
}
return null;
}
}
- 测试
2.springboot整合shiro完全分离
前后端完成分离,后端返回给访问者的是json数据
该项目中有三个地方需要改造返回json数据:
1、认证成功或认证失败时
2、出现异常时,例如权限不足时
3、未登录访问时
- ①创建返回的json数据格式的Result类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
private Integer code;
private String msg;
private Object data;
}
- ②改造Controller
将Controller层的@Controller都改成@RestController,将@ControllerAdvice改成@RestControllerAdvice
LoginController
@PostMapping("login")
public Result login(String username, String userpwd){
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token=new UsernamePasswordToken(username,userpwd);
try {
subject.login(token);//realm的认证功能
//getPrincipl()可以得到SimpleAuthenticationInfo中的数据
Object user = subject.getPrincipal();
return new Result(2000,"登陆成功",user);
}catch (Exception e){
return new Result(5000,"登陆失败",null);
}
}
- ②改PageController
PageController
@RestController
public class PageController {
@GetMapping("/tologin")
public Result loginPage(){
return new Result(5002,"请先登录",null);
}
}
- ExceptionController 异常处理类
@RestControllerAdvice
public class MyHandlerException {
@ExceptionHandler(value = UnauthorizedException.class)
public ResultunauthorizedException(){
return new Result(5001,"权限不足",null);
}
}
- 测试