入門demo
index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首頁</h1>
<div th:text="${msg}"></div>
<a th:href="@{/user/add}">add</a>|<a th:href="@{/user/update}">update</a>
</body>
</html>
add.html跟update.html只有一個名字
依賴;
<!-- thymeleaf模板-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
<!-- shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.4.1</version>
</dependency>
Controller
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class MyController {
@RequestMapping({"/", "/index"})
public String toIndex(Model model) {
model.addAttribute("msg", "hello Shiro");
return "index";
}
@RequestMapping("/user/add")
public String add(){
return "user/add";
}
@RequestMapping("/user/update")
public String update(){
return "user/update";
}
@RequestMapping("/toLogin")
public String toLogin() {
return "login";
}
}
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;
@Configuration
public class ShiroConfig {
//3、ShiroFilterFactoryBean,在容器中通過@Qualifier註解將securityManager傳遞過來,
//這裏有一個坑,一定要在Bean註解添加name屬性,否則報錯找不到shiroFilterFactoryBean
@Bean(name = "shiroFilterFactoryBean")
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//設置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
return shiroFilterFactoryBean;
}
//2、DefaultWebSecurityManager,在容器中通過@Qualifier註解將UserRealm傳遞過來,
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//關聯UserRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//1、創建realm對象,需自定義類
@Bean(name = "userRealm")
public UserRealm userRealm(){
return new UserRealm();
}
}
自定義的Realm對象
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
public class UserRealm extends AuthorizingRealm {
//授權
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("執行了授權");
return null;
}
//認證
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("執行了認證");
return null;
}
}
啓動點擊對於的a標籤即可實現跳轉,因爲還沒使用過濾,所有可以訪問所有頁面
實現登錄攔截
在getShiroFilterFactoryBean方法內添加shiro內置過濾器
//3、ShiroFilterFactoryBean,在容器中通過@Qualifier註解將securityManager傳遞過來,
//這裏有一個坑,一定要在Bean註解添加name屬性,否則報錯找不到shiroFilterFactoryBean
@Bean(name = "shiroFilterFactoryBean")
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//設置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
//添加shiro內置過濾器
/*
anon:無需認證即可訪問
authc:必須認證了才能訪問
user:必須擁有 記住我 功能才能訪問
perms:擁有對某個資源權限才能訪問
role:擁有某個角色權限才能訪問
*/
//攔截
Map<String, String> filterMap=new LinkedHashMap<>();
//user下所有的頁面都需要認證才能訪問
filterMap.put("/user/*","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
//設置登錄請求
shiroFilterFactoryBean.setLoginUrl("/toLogin");
return shiroFilterFactoryBean;
}
login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p th:text="${msg}" style="color: red"></p>
<form th:action="@{/login}">
<p>用戶名:<input type="text" name="username"></p>
<p>密碼:<input type="password" name="password"></p>
<p><input type="submit"></p>
</form>
</body>
</html>
此時點擊add或者update會跳轉到login頁面
實現用戶認證
Controller加入
@RequestMapping("/login")
public String login(String username, String password, Model model) {
//獲取當前的用戶
Subject subject = SecurityUtils.getSubject();
//封裝用戶登錄的數據
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
//執行登錄方法,沒有異常就代表登錄上了
subject.login(token);
return "index";
} catch (UnknownAccountException e) {//用戶名不存在報錯
model.addAttribute("msg", "用戶名錯誤");
return "login";
} catch (IncorrectCredentialsException e) {
model.addAttribute("msg", "密碼錯誤");
return "login";
}
}
UserRealm加入
//認證
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("執行了認證");
//數據庫賬戶密碼
String name="root";
String password="123456";
UsernamePasswordToken userToken=(UsernamePasswordToken)token;
if(!userToken.getUsername().equals(name)){
return null;//如果沒有匹配到返回null會拋出異常
}
//密碼認證,shiro做
return new SimpleAuthenticationInfo("", password, "");
}
整合Mybatis
依賴
<!-- mysql驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<!-- mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<!-- lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<!-- log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
application.properties,mybatis配置文件
mybatis.type-aliases-package=com.ykk.pojo
mybatis.mapper-locations=classpath:mapper/*.xml
application.yml
spring:
datasource:
username: root
password: root
#?serverTimezone=UTC解決時區的報錯
url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
#Spring Boot 默認是不注入這些屬性值的,需要自己綁定
#druid 數據源專有配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置監控統計攔截的filters,stat:監控統計、log4j:日誌記錄、wall:防禦sql注入
#如果允許時報錯 java.lang.ClassNotFoundException: org.apache.log4j.Priority
#則導入 log4j 依賴即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
User實體類
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
}
UserMapper
@Repository
@Mapper
public interface UserMapper {
User queryUserByName(String name);
}
UserMapper.XML(要放到resource下,不然會找不到資源)
<?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.ykk.mapper.UserMapper">
<select id="queryUserByName" parameterType="String" resultType="User">
select * from User where name = #{name};
</select>
</mapper>
UserService
public interface UserService {
public User queryUserByName(String name);
}
UserServiceImpl
@Service
public class UserServiceImpl implements UserService{
@Autowired
UserMapper userMapper;
@Override
public User queryUserByName(String name) {
return userMapper.queryUserByName(name);
}
}
修改自定義的UserRealm中認證方法
//認證
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("執行了認證");
UsernamePasswordToken userToken=(UsernamePasswordToken)token;
//連接真實數據庫,通過@Autowired注入userService
User user = userService.queryUserByName(userToken.getUsername());
if(user==null){//沒有這個用戶
return null;
}
//密碼認證,shiro做
return new SimpleAuthenticationInfo("", user.getPwd(), "");
}
實現請求授權
數據庫加入perms字段
//自定義realm
public class UserRealm extends AuthorizingRealm {
@Autowired
UserServiceImpl userService;
//授權
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("執行了授權");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//添加權限,一般從數據庫表獲取,這裏是寫死了
//info.addStringPermission("user:add");
//拿到當前登錄的對象
Subject subject = SecurityUtils.getSubject();
User currentUser = (User) subject.getPrincipal();
//設置當前用戶權限
info.addStringPermission(currentUser.getPerms());
return info;
}
//認證
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("執行了認證");
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
//連接真實數據庫
User user = userService.queryUserByName(userToken.getUsername());
if (user == null) {//沒有這個用戶
return null;
}
//密碼認證,shiro做。這裏傳過去的user在doGetAuthorizationInfo方法可以拿到
return new SimpleAuthenticationInfo(user, user.getPwd(), "");
}
}
//3、ShiroFilterFactoryBean,在容器中通過@Qualifier註解將securityManager傳遞過來,
//這裏有一個坑,一定要在Bean註解添加name屬性,否則報錯找不到shiroFilterFactoryBean
@Bean(name = "shiroFilterFactoryBean")
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//設置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
//添加shiro內置過濾器
/*
anon:無需認證即可訪問
authc:必須認證了才能訪問
user:必須擁有 記住我 功能才能訪問
perms:擁有對某個資源權限才能訪問
role:擁有某個角色權限才能訪問
*/
//攔截
Map<String, String> filterMap=new LinkedHashMap<>();
//授權,沒授權會轉到對於的頁面
filterMap.put("/user/add","perms[user:add]");
filterMap.put("/user/update","perms[user:update]");
//user目錄下的頁面需要驗證才能訪問
filterMap.put("/user/*","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
//設置登錄請求
shiroFilterFactoryBean.setLoginUrl("/toLogin");
return shiroFilterFactoryBean;
}
整合thymeleaf
依賴
<!-- shiro-thymeleaf整合-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
命名空間
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro"
index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首頁</h1>
<div th:if="${session.loginUser==null}">
<a th:href="@{/toLogin}">登錄</a>
</div>
<p th:text="${msg}"></p>
<hr>
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">add</a>
</div>
<div shiro:hasPermission="user:update">
<a th:href="@{/user/update}">update</a>
</div>
</body>
</html>
示例源碼:https://gitee.com/ak2019/learning.git
springboot整合shiro源碼,非常值得學習:https://mp.weixin.qq.com/s/rPNxAnuRzD3ec_GX_V4RmQ