SpringBoot与Shiro整合
修改pom.xml:
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<!-- 继承Spring Boot的默认父工程 --> <!-- Spring Boot 父工程 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.4.RELEASE</version> </parent>
<groupId>com.mxj</groupId> <artifactId>springboot-shiro</artifactId> <version>0.0.1-SNAPSHOT</version> </project> |
1.1. 导入web支持
修改pom.xml
<!-- 导入依赖 --> <dependencies> <!-- 导入web支持:SpringMVC开发支持,Servlet相关的程序 --> <!-- web支持,SpringMVC, Servlet支持等 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> |
1.2. 编写测试Controller类
package com.mxj.controller;
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody;
@Controller public class UserController {
/** * 测试方法 */ @RequestMapping("/hello") @ResponseBody public String hello(){ System.out.println("UserController.hello()"); return "ok"; } }
|
1.3. 编写SpringBoot启动类
package com.mxj;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
/** * SpringBoot启动类 * @author MA * */ @SpringBootApplication public class Application {
public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
|
1.4. 导入thymeleaf页面模块
l 引入thymeleaf依赖
<!-- 导入thymeleaf依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> |
l 在Controller添加测试方法
/** * 测试thymeleaf */ @RequestMapping("/testThymeleaf") public String testThymeleaf(Model model){ //把数据存入model model.addAttribute("name", "mxj"); //返回test.html return "test"; } |
l 建立test.html页面
在src/main/resource目录下创建templates目录,然后创建test.html页面
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>测试Thymeleaf的使用</title> </head> <body> <h3 th:text="${name}"></h3> </body> </html> |
在thymeleaf3.0以前对页面标签语法要求比较严格,开始标签必须有对应的结束标签。
如果希望页面语法不严谨,但是也能够运行成功,可以把thymeleaf升级为3.0或以上版本。
升级thymeleaf3.0.2版本:
<!-- 修改参数 --> <properties> <!-- 修改JDK的编译版本为1.8 --> <java.version>1.8</java.version> <!-- 修改thymeleaf的版本 --> <thymeleaf.version>3.0.2.RELEASE</thymeleaf.version> <thymeleaf-layout-dialect.version>2.0.4</thymeleaf-layout-dialect.version> </properties> |
2. Spring Boot与Shiro整合实现用户认证
2.1. 分析Shiro的核心API
Subject: 用户主体(把操作交给SecurityManager)
SecurityManager:安全管理器(关联Realm)
Realm:Shiro连接数据的桥梁
2.2. Spring Boot整合Shiro
2.2.1. 导入shiro与spring整合依赖
修改pom.xml
<!-- shiro与spring整合依赖 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> |
2.2.2. 自定义Realm类
package com.mxj.shiro;
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection;
/** * 自定义Realm * @author MA * */ public class UserRealm extends AuthorizingRealm{
/** * 执行授权逻辑 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) { System.out.println("执行授权逻辑"); return null; }
/** * 执行认证逻辑 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException { System.out.println("执行认证逻辑"); return null; }
}
|
2.2.3. 编写Shiro配置类(*)
package com.mxj.shiro;
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;
/** * Shiro的配置类 * @author MA * */ @Configuration public class ShiroConfig {
/** * 创建ShiroFilterFactoryBean */ @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器 shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean; }
/** * 创建DefaultWebSecurityManager */ @Bean(name="securityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //关联realm securityManager.setRealm(userRealm); return securityManager; }
/** * 创建Realm */ @Bean(name="userRealm") public UserRealm getRealm(){ return new UserRealm(); } }
|
2.3. 使用Shiro内置过滤器实现页面拦截
package com.mxj.shiro;
import java.util.LinkedHashMap; 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;
/** * Shiro的配置类 * @author MA * */ @Configuration public class ShiroConfig {
/** * 创建ShiroFilterFactoryBean */ @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器 shiroFilterFactoryBean.setSecurityManager(securityManager);
//添加Shiro内置过滤器 /** * Shiro内置过滤器,可以实现权限相关的拦截器 * 常用的过滤器: * anon: 无需认证(登录)可以访问 * authc: 必须认证才可以访问 * user: 如果使用rememberMe的功能可以直接访问 * perms: 该资源必须得到资源权限才可以访问 * role: 该资源必须得到角色权限才可以访问 */ Map<String,String> filterMap = new LinkedHashMap<String,String>(); /*filterMap.put("/add", "authc"); filterMap.put("/update", "authc");*/
filterMap.put("/testThymeleaf", "anon");
filterMap.put("/*", "authc");
//修改调整的登录页面 shiroFilterFactoryBean.setLoginUrl("/toLogin");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean; }
/** * 创建DefaultWebSecurityManager */ @Bean(name="securityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //关联realm securityManager.setRealm(userRealm); return securityManager; }
/** * 创建Realm */ @Bean(name="userRealm") public UserRealm getRealm(){ return new UserRealm(); } }
|
2.4. 实现用户认证(登录)操作
2.4.1. 设计登录页面
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>登录页面</title> </head> <body> <h3>登录</h3> <form method="post" action="login"> 用户名:<input type="text" name="name"/><br/> 密码:<input type="password" name="password"/><br/> <input type="submit" value="登录"/> </form> </body> </html> |
2.4.2. 编写Controller的登录逻辑
/** * 登录逻辑处理 */ @RequestMapping("/login") public String login(String name,String password,Model model){
/** * 使用Shiro编写认证操作 */ //1.获取Subject Subject subject = SecurityUtils.getSubject();
//2.封装用户数据 UsernamePasswordToken token = new UsernamePasswordToken(name,password);
//3.执行登录方法 try { subject.login(token);
//登录成功 //跳转到test.html return "redirect:/testThymeleaf"; } catch (UnknownAccountException e) { //e.printStackTrace(); //登录失败:用户名不存在 model.addAttribute("msg", "用户名不存在"); return "login"; }catch (IncorrectCredentialsException e) { //e.printStackTrace(); //登录失败:密码错误 model.addAttribute("msg", "密码错误"); return "login"; } } |
2.4.3. 编写Realm的判断逻辑
package com.mxj.shiro;
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.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection;
/** * 自定义Realm * @author MA * */ public class UserRealm extends AuthorizingRealm{
/** * 执行授权逻辑 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) { System.out.println("执行授权逻辑"); return null; }
/** * 执行认证逻辑 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException { System.out.println("执行认证逻辑");
//假设数据库的用户名和密码 String name = "eric"; String password = "123456";
//编写shiro判断逻辑,判断用户名和密码 //1.判断用户名 UsernamePasswordToken token = (UsernamePasswordToken)arg0; if(!token.getUsername().equals(name)){ //用户名不存在 return null;//shiro底层会抛出UnKnowAccountException }
//2.判断密码 return new SimpleAuthenticationInfo("",password,""); }
}
|
2.5. 整合MyBatis实现登录
2.5.1. 导入mybatis相关的依赖
<!-- 导入mybatis相关的依赖 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.9</version> </dependency> <!-- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- SpringBoot的Mybatis启动器 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.1.1</version> </dependency> |
2.5.2. 配置application.properties
位置:src/main/resources目录下
spring.datasource.driverClassName=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/ssm spring.datasource.username=root spring.datasource.password=123456
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
mybatis.type-aliases-package=com.mxj.dao |
2.5.3. 编写User实体
package com.mxj.dao;
public class User { private Integer id; private String name; private String password; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; }
}
|
2.5.4. 编写UserMapper接口
package com.mxj.mapper;
import com.itheima.domain.User;
public interface UserMapper {
public User findByName(String name); }
|
2.5.5. 编写UserMapper.xml映射文件
<?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"> <!-- 该文件存放CRUD的sql语句 --> <mapper namespace="com.itheima.mapper.UserMapper">
<select id="findByName" parameterType="string" resultType="user"> SELECT id, NAME, PASSWORD FROM user where name = #{value} </select> </mapper> |
2.5.6. 编写业务接口和实现
接口:
package com.mxj.service;
import com.itheima.domain.User;
public interface UserService {
public User findByName(String name); }
|
实现;
package com.mxj.service.impl;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;
import com.itheima.domain.User; import com.itheima.mapper.UserMapper; import com.itheima.service.UserService;
@Service public class UserServiceImpl implements UserService{
//注入Mapper接口 @Autowired private UserMapper userMapper;
@Override public User findByName(String name) { return userMapper.findByName(name); }
}
|
2.5.7. 添加@MapperScan注解
package com.mxj;
import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
/** * SpringBoot启动类 * @author MA * */ @SpringBootApplication @MapperScan("com.mxj.mapper") public class Application {
public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
|
2.5.8. 修改UserRealm
package com.mxj.shiro;
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.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.springframework.beans.factory.annotation.Autowired;
import com.mxj.dao.User; import com.mxj.service.UserService;
/** * 自定义Realm * @author MA * */ public class UserRealm extends AuthorizingRealm{
/** * 执行授权逻辑 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) { System.out.println("执行授权逻辑"); return null; }
@Autowired private UserService userSerivce;
/** * 执行认证逻辑 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException { System.out.println("执行认证逻辑");
//编写shiro判断逻辑,判断用户名和密码 //1.判断用户名 UsernamePasswordToken token = (UsernamePasswordToken)arg0;
User user = userSerivce.findByName(token.getUsername());
if(user==null){ //用户名不存在 return null;//shiro底层会抛出UnKnowAccountException }
//2.判断密码 return new SimpleAuthenticationInfo("user.getName()",user.getPassword(),""); }
}
|
3. Spring Boot与Shiro整合实现用户授权
3.1. 使用Shiro内置过滤器拦截资源
/** * 创建ShiroFilterFactoryBean */ @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器 shiroFilterFactoryBean.setSecurityManager(securityManager);
//添加Shiro内置过滤器 /** * Shiro内置过滤器,可以实现权限相关的拦截器 * 常用的过滤器: * anon: 无需认证(登录)可以访问 * authc: 必须认证才可以访问 * user: 如果使用rememberMe的功能可以直接访问 * perms: 该资源必须得到资源权限才可以访问 * role: 该资源必须得到角色权限才可以访问 */ Map<String,String> filterMap = new LinkedHashMap<String,String>(); /*filterMap.put("/add", "authc"); filterMap.put("/update", "authc");*/
filterMap.put("/testThymeleaf", "anon"); //放行login.html页面 filterMap.put("/login", "anon");
//授权过滤器 //注意:当前授权拦截后,shiro会自动跳转到未授权页面 filterMap.put("/add", "perms[user:add]");
filterMap.put("/*", "authc");
//修改调整的登录页面 shiroFilterFactoryBean.setLoginUrl("/toLogin"); //设置未授权提示页面 shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean; } |
3.2. 完成Shiro的资源授权
UserRealm:
/** |
4. thymeleaf和shiro标签整合使用
4.1. 导入thymeleaf扩展坐标
<!-- thymel对shiro的扩展坐标 --> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency> |
4.2. 配置ShiroDialect
在ShiroConfig类里面添加getShiroDialect方法
/** * 配置ShiroDialect,用于thymeleaf和shiro标签配合使用 */ @Bean public ShiroDialect getShiroDialect(){ return new ShiroDialect(); } |
4.3. 在页面上使用shiro标签
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>测试Thymeleaf的使用</title> </head> <body> <h3 th:text="${name}"></h3>
<hr/> <div shiro:hasPermission="user:add"> 进入用户添加功能: <a href="add">用户添加</a><br/> </div> <div shiro:hasPermission="user:update"> 进入用户更新功能: <a href="update">用户更新</a><br/> </div> <a href="toLogin">登录</a> </body> </html> |