有关安全的还有Security,OAuth2等,这次的主角Shiro
整合shiro
1.引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>shiro</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>shiro</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.4.0</version>
</dependency>
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--在thymeleaf中使用shiro标签-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.shiro的基本配置
shiro:
enabled: true #开启shiro配置,默认为true
web:
enabled: true #开启shiro web 配置默认为true
loginUrl: /login #登录地址
successUrl: /index #登陆成功地址,默认 "/"
unauthorizedUrl: /unauthorized #未授权默认跳转地址
sessionManager:
sessionIdCookieEnabled: true #是否允许通过cookie实现会话跟踪,默认为true
sessionIdUrlRewritingEnabled: true #允许通过URL参数实现会话跟踪,如果网站支持cookie,可以关闭此选项,默认为true
spring:
thymeleaf:
cache: true #是否开启缓存,开发时可设置为false,默认为true
check-template: true #检查模板是否存在,默认为true
check-template-location: true #检查模板位置是否存在,默认为true
encoding: UTF-8 #模板文件编码
prefix: classpath:/templates/ #模板文件位置
servlet:
content-type: text/html #content-type配置
suffix: .html #模板文件后缀
3.编写ShiroConfig
package com.example.shiro.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.text.TextConfigurationRealm;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ShiroConfig {
@Bean
public Realm realm(){
TextConfigurationRealm realm = new TextConfigurationRealm();
realm.setUserDefinitions("sang=123,user\n admin=123,admin");
realm.setRoleDefinitions("admin=read,write\n user=read");
return realm;
}
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition(){
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
chainDefinition.addPathDefinition("/login","anon");
chainDefinition.addPathDefinition("/doLogin","anon");
chainDefinition.addPathDefinition("/logout","logout");
chainDefinition.addPathDefinition("/**","authc");
return chainDefinition;
}
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
}
4.controller
package com.example.shiro.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class UserController {
@PostMapping("/doLogin")
public String doLogin(String username, String password, Model model){
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
} catch (AuthenticationException e) {
model.addAttribute("error","用户名或密码错误!");
return "login";
}
return "redirect:/index";
}
@RequiresRoles("admin")
@GetMapping("/admin")
public String admin(){
return "admin";
}
@RequiresRoles(value={"admin","user"},logical= Logical.OR)
@GetMapping("/user")
public String user(){
return "user";
}
}
5.WebMvcConfig
package com.example.shiro.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
registry.addViewController("index").setViewName("index");
registry.addViewController("/unauthorized").setViewName("unauthorized");
}
}
创建全局处理异常
package com.example.shiro.exception;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
import javax.security.sasl.AuthenticationException;
@ControllerAdvice
public class ExceptionController {
@ExceptionHandler(AuthenticationException.class)
public ModelAndView error(AuthenticationException e){
ModelAndView mv = new ModelAndView("unauthorized");
mv.addObject("error",e.getMessage());
return mv;
}
}
创建页面
login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<form action="/doLogin" method="post">
<input type="text" name="username">
<input type="password" name="password">
<div th:text="${error}"></div>
<input type="submit" value="登录">
</form>
</div>
</body>
</html>
index.html
<!DOCTYPE html>
<html lang="en" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>Hello,<shiro:principal></shiro:principal></h3>
<h3><a href="/logout">注销登录</a></h3>
<h3><a shiro:hasRole="admin" href="/admin">管理员页面</a></h3>
<h3><a shiro:hasAnyRoles="admin,user" href="/user">普通用户登录页面</a></h3>
</body>
</html>
user.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>管理员页面</h1>
</body>
</html>
unauthorized.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<h3>未获授权,非法访问</h3>
<h3 th:text="${error}"></h3>
</div>
</body>
</html>
登录结果