簡單的shiro應用(學習狂神說)
阿新 • • 發佈:2020-11-04
一、上程式碼
controller
package com.wxl.controller; @Controller public class IndexController { @RequestMapping({"/","index"}) public String toIndex(Model model){ model.addAttribute("welcome","歡迎!"); return "index"; } @RequestMapping("/toLogin") public String toLogin(){ return "login"; } //templates下面的login.html @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); //執行登入方法,如果沒有異常就說明ok了 return "index"; } catch (UnknownAccountException e){ //使用者名稱不存在 model.addAttribute("msg", "使用者名稱錯誤"); return "login"; } catch (IncorrectCredentialsException e){ //密碼不存在 model.addAttribute("msg","密碼錯誤"); return "login"; } catch (AuthenticationException e) { e.printStackTrace(); return "login"; } } @RequestMapping("/unauth") @ResponseBody public String unauth(){ return "您沒有許可權訪問該頁面!"; } @RequestMapping("/user/add") public String toAdd(){ return "user/add"; } @RequestMapping("/user/update") public String toUpdate(){ return "user/update"; } }
UserRealm
package com.wxl.config; //自定義UserRealm extends AuthorizingRealm public class UserRealm extends AuthorizingRealm { @Autowired UserService userService; //授權 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { System.out.println("執行了=>授權doGetAuthorizationInfo..."); //SimpleAuthorizationInfo SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //拿到當前登入的物件 Subject subject = SecurityUtils.getSubject(); User principal = (User) subject.getPrincipal(); //設定當前使用者的許可權,許可權從資料庫中讀取 info.addStringPermission(principal.getPerms()); return info; } //認證 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("執行了=>認證doGetAuthenticationInfo..."); //使用者名稱,密碼~ 資料庫中取 UsernamePasswordToken usertoken = (UsernamePasswordToken) token; User user = userService.QueryUserByName(usertoken.getUsername()); if (user==null){ return null; //丟擲異常UnknownAccountException } /*把登入成功的資訊存到session裡面,用於頁面的判斷*/ Subject current = SecurityUtils.getSubject(); Session session = current.getSession(); session.setAttribute("loginUser",user); //密碼認證 shiro做~ return new SimpleAuthenticationInfo(user,user.getPwd(),""); } }
ShiroConfig
package com.wxl.config; @Configuration public class ShiroConfig { //ShiroFilterFactoryBean 第三步 //DefaultWebSecurityManager 第二步 //建立realm物件 需要自定義 第一步 @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){ ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean(); //設定安全管理器 filterFactoryBean.setSecurityManager(securityManager); //新增shiro的內建過濾器 /* 過濾規則 : anon : 無需認證就可以訪問 authc : 必須認證才能訪問 user : 必須擁有 記住我 才能使用 perms : 擁有對某個資源的許可權才能訪問 role : 擁有某個角色許可權才能訪問 */ /*filterMap.put("/toLogin","anon"); filterMap.put("/user/add","authc"); filterMap.put("/user/update","authc");*/ //攔截 Map<String, String> filterMap = new LinkedHashMap<>(); //filterMap中要先授權再攔截 //授權 filterMap.put("/user/add","perms[user:add]"); filterMap.put("/user/update","perms[user:update]"); //認證才能訪問add和update頁面 filterMap.put("/user/*","authc"); //萬用字元 user下面的所有路徑 filterFactoryBean.setFilterChainDefinitionMap(filterMap); //設定鏈過濾 //設定登入的請求 filterFactoryBean.setLoginUrl("/toLogin"); //設定未授權的請求 filterFactoryBean.setUnauthorizedUrl("/unauth"); return filterFactoryBean; } @Bean(name = "securityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //關聯UserRealm securityManager.setRealm(userRealm); return securityManager; } @Bean public UserRealm userRealm(){ return new UserRealm(); } /*這裡做shiro和thymeleaf整合*/ @Bean public ShiroDialect getShiroDialect(){ return new ShiroDialect(); } }
Dao
User QueryUserByName(String name);
User
package com.wxl.pojo;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
private String perms;
}
頁面
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:text="${welcome}"></div>
<!--把登入資訊存到session裡面,session為空顯示登入,否則不顯示-->
<div th:if="${session.loginUser==null}">
<a th:href="@{/toLogin}">登入</a>
</div>
<div shiro:hasPermission="user:add"> <!--使用者擁有user:add這個許可權就顯示-->
<a th:href="@{/user/add}">新增</a>
</div>
<div shiro:hasPermission="user:update">
<a th:href="@{/user/update}">更新</a>
</div>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登入頁面</title>
</head>
<body>
<div style="width:100%;">
<h3 style="width:100%;text-align: center">登入</h3>
</div>
<div style="width:100%;text-align: center">
<p th:text="${msg}" style="color: red"></p>
<form action="/login" >
<p>使用者名稱:<input type="text" name="username"></p>
<p>密 碼:<input type="password" name="password"></p>
<p><input type="submit"></p>
</form>
</div>
</body>
</html>
二、準備工作
- pom.xml
<dependencies>
<!--shiro-->
<!--
subject : 使用者
sercurityManager : 管理所有使用者
realm : 連線資料
-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.4.1</version>
</dependency>
<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>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>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
<!--thymeleaf和shiro整合包-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
</dependencies>
- application.yml
mybatis:
mapper-locations: classpath:mapper/*.xml
config-location:
map-underscore-to-camel-case: true #開啟mybatis的駝峰命名
type-aliases-package: com.wxl.pojo
spring:
datasource:
druid:
username: root
password: root
#?serverTimezone=UTC解決時區的報錯
url: jdbc:mysql://localhost:3306/demo_datebase?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
#Spring Boot 預設是不注入這些屬性值的,需要自己繫結
#druid 資料來源專有配置
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
time-between-eviction-runs-millis: 60000 #有兩個含義: 1) Destroy執行緒會檢測連線的間隔時間2) testWhileIdle的判斷依據,詳細看testWhileIdle屬性的說明
min-evictable-idle-time-millis: 300000
validation-query: select 1 from dual #用來檢測連線是否有效的sql,要求是一個查詢語句。如果validationQuery為null,testOnBorrow、testOnReturn、testWhileIdle都不會其作用
test-on-return: false #申請連線時執行validationQuery檢測連線是否有效,做了這個配置會降低效能。
test-on-borrow: false #歸還連線時執行validationQuery檢測連線是否有效,做了這個配置會降低效能
test-while-idle: true #建議配置為true,不影響效能,並且保證安全性。申請連線的時候檢測,如果空閒時間大於timeBetweenEvictionRunsMillis,執行validationQuery檢測連線是否有效。
pool-prepared-statements: true #是否快取preparedStatement,也就是PSCache。PSCache對支援遊標的資料庫效能提升巨大,比如說oracle。在mysql下建議關閉。
max-pool-prepared-statement-per-connection-size: 20 #指定每個連線上PSCache的大小
#配置監控統計攔截的filters, stat: 監控統計,wall: 防禦sql注入,log4j: 日誌記錄
#如果允許時報錯 java.lang.ClassNotFoundException: org.apache.log4j.Priority
#則匯入log4j 依賴即可
filters: stat,wall,log4j #屬性型別是字串,通過別名的方式配置擴充套件外掛,常用的外掛有: 監控統計用的filter:stat 日誌用的filter:log4j 防禦sql注入的filter:wall
use-global-data-source-stat: true
connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500