一宪摧、相關(guān)框架簡(jiǎn)介
1.1 spring boot
spring boot是由Pivotal團(tuán)隊(duì)開發(fā)的全新框架谈截,其主要目的是使搭建spring web項(xiàng)目變得簡(jiǎn)單违帆、快速挣磨,能快速進(jìn)入到項(xiàng)目的業(yè)務(wù)邏輯開發(fā)。其主要的配置是在application命名的文件中劣摇,主要有application.properties和application.yml兩種配置方式珠移,spring boot項(xiàng)目啟動(dòng)時(shí)會(huì)默認(rèn)到項(xiàng)目路徑下去尋找application的文件并讀取內(nèi)容。
1.2 Shiro
Apache Shiro是一個(gè)強(qiáng)大且易用的Java安全框架,執(zhí)行身份驗(yàn)證末融、授權(quán)钧惧、密碼學(xué)和會(huì)話管理。shiro主要由三個(gè)主要組建組成:Subject(當(dāng)前操作用戶勾习,不僅僅是人或事物的對(duì)象垢乙,還可能是線程),SecurityManager(shiro的核心框架语卤,用來(lái)管理內(nèi)部組建實(shí)例并提供安全管理的服務(wù)),Realm(是shiro和安全數(shù)據(jù)數(shù)據(jù)的橋梁,當(dāng)對(duì)用戶進(jìn)行授權(quán)或身份驗(yàn)證時(shí)粹舵,shiro會(huì)從配置的Realm中查找用戶和其相關(guān)權(quán)限)钮孵。Realm相當(dāng)于我們開發(fā)過(guò)程中的dao層,它封裝了數(shù)據(jù)連接的細(xì)節(jié)眼滤。
1.3 Swagger
swagger是目前最受歡迎的Rest Apis文檔生成工具之一巴席,它可以生成支持互動(dòng)類型的API控制臺(tái),生成可以在不能平臺(tái)運(yùn)行的客戶端SDK诅需,Swagger 文檔提供了一個(gè)方法漾唉,使我們可以用指定的 JSON 或者 YAML 摘要來(lái)描述你的 API,包括了比如 names堰塌、order 等 API 信息等優(yōu)點(diǎn)赵刑。
二、基本接口的提供
以u(píng)ser為列场刑,提供相關(guān)接口般此,具體實(shí)現(xiàn)忽略。controller代碼如下:
package com.why.greenhouse.back.user.controller;
import com.why.greenhouse.back.config.filter.ShiroProperties;
import com.why.greenhouse.back.user.entity.User;
import com.why.greenhouse.back.user.service.UserService;
import com.why.greenhouse.back.user.model.request.UserRequest;
import com.why.greenhouse.back.user.model.response.UserResponse;
import java.util.List;
import java.util.Map;
import com.why.greenhouse.back.utils.UserUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* 描述:user控制層
*
* @author why
* @date 2018-07-07 14:17:51
*/
@RestController
@RequestMapping(path = "user")
public class UserController {
@Autowired
private UserService userService;
/**
* 描述:根據(jù)Id 查詢
*
* @param id userid
*/
@GetMapping("findById")
public UserResponse findById(@RequestParam("id") Long id) throws Exception {
UserResponse userResponse = userService.queryUserById(id);
return userResponse;
}
/**
* 描述:創(chuàng)建user
*
* @param userRequest
*/
@PostMapping("add")
public void add(@RequestBody UserRequest userRequest) throws Exception {
userService.addUser(userRequest);
}
/**
* 描述:刪除user
*
* @param id userid
*/
@DeleteMapping("deleteById")
public void deleteById(@RequestParam("id") Long id) throws Exception {
userService.deleteUser(id);
}
/**
* 描述:更新user
*
* @param userRequest userRequest
*/
@PostMapping("update")
public void update(@RequestBody UserRequest userRequest) throws Exception {
userService.updateUser(userRequest);
}
@Autowired
private ShiroProperties shiroProperties;
/**
* 描述:查詢所有user
*/
@PostMapping("queryAll")
public List<UserResponse> queryAll() throws Exception {
for(String s : shiroProperties.getFilterRules()){
System.out.println(s);
}
return userService.queryAll();
}
/**
* 描述:查詢所有user
*/
@GetMapping("login")
public void login(){
// public void login(@RequestParam(name = "name") String name){
// User user = userService.queryByName(name);
// if (user==null){
// throw new RuntimeException("不存在改用戶");
// }
System.out.println("的點(diǎn)點(diǎn)滴滴多多多");
}
}
三牵现、shiro Filter相關(guān)代碼
添加相關(guān)依賴,依賴中有一些自己測(cè)試用的包铐懊,該功能沒有用到
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.why</groupId>
<artifactId>greenhouse</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>common</module>
<module>front</module>
<module>back</module>
<module>core</module>
</modules>
<name>Green House Server</name>
<description>Green House Server</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<swagger.version>2.7.0</swagger.version>
</properties>
<dependencies>
<!-- jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--cache-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!--lombok依賴-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- the follow depends is for junit test and api test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 數(shù)據(jù)庫(kù)MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.21</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
<exclusions>
<exclusion>
<artifactId>reflections</artifactId>
<groupId>org.reflections</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.16</version>
</dependency>
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
</dependencies>
</project>
通過(guò)@Bean方式注入自己定義的攔截和shiro相關(guān)的類
package com.why.greenhouse.back.config.filter;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author why
* 攔截器實(shí)例化
*/
@Configuration
@ConditionalOnProperty(name = "shiro.web.enabled", matchIfMissing = true)
public class LoginFilterBean{
@Autowired
private ShiroProperties shiroProperties;
@Bean
public LoginFilter loginFilter(){
return new LoginFilter();
}
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
return securityManager;
}
@ConditionalOnMissingBean
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
//打印日志便于查看是否進(jìn)入該攔截器
System.out.println("LoginFilterBean.shirFilter()");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
Map<String,Filter> filterMap = new HashMap<>(20);
//定義攔截器名稱
filterMap.put("hasToken",new LoginFilter());
shiroFilterFactoryBean.setFilters(filterMap);
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String,String> filterChainMap = new HashMap<>(20);
//從配置文件中自定義攔截規(guī)則的相關(guān)配置信息
List<String> filters = shiroProperties.getFilterRules();
if (null!=filters){
for (String str: filters) {
String[] temp = str.split("==>");
if (temp.length != 2) {
throw new IllegalStateException("過(guò)濾規(guī)則配置不正確,格式:url==>filters");
}
filterChainMap.put(temp[0],temp[1]);
}
}
//把自己定義的攔截相關(guān)規(guī)則交給shiro管理
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
shiroFilterFactoryBean.setLoginUrl("/user/login");
shiroFilterFactoryBean.setSuccessUrl("/");
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
return shiroFilterFactoryBean;
}
/**
* 加入注解的使用,不加入這個(gè)注解不生效
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
application.yml文件相關(guān)配置信息如下:
server:
tomcat:
uri-encoding: UTF-8
max-threads: 3000
port: 15030
compression:
enabled: true
spring:
datasource:
password: 1pTVg0ld@1909
tomcat:
max-idle: 10
min-idle: 5
test-on-borrow: false
test-while-idle: true
time-between-eviction-runs-millis: 18800
validation-query: SELECT 1
initial-size: 5
max-wait: 3000
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/why?useUnicode=true&characterEncoding=utf8&autoReconnect=true&failOver
username: root
hikari:
read-only:
messages:
cache-seconds: -1
always-use-message-format: false
fallback-to-system-locale: true
basename: i18n/messages
encoding: UTF-8
jpa:
show-sql: true
database: MYSQL
generate-ddl: false
hibernate:
ddl-auto: update
properties:
globally_quoted_identifiers: true
use_query_cache: false
hibernate:
dialect: org.hibernate.dialect.MySQL5Dialect
shiro:
filter-rules:
- /swagger-ui.html==>anon
- /swagger-resources/**==>anon
- //webjars/**==>anon
- /v2/**==>anon
- /user/login/**==>anon
- /user/queryAll/**==>anon
- /**==>authc
這個(gè)時(shí)候還訪問不了swagger頁(yè)面瞎疼,需要對(duì)swagger進(jìn)行相關(guān)的配置科乎,配置類如下:
package com.why.greenhouse.back.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
/**
* swagger配置
* @author why
*/
@Configuration
@EnableSwagger2
public class SwaggerConfiguration {
@Bean
public Docket createRestApi(){
//add head start
ParameterBuilder tokenPar = new ParameterBuilder();
List<Parameter> pars = new ArrayList<>();
tokenPar.name("token").description("token").modelRef(new ModelRef("string")).parameterType("header").required(false).build();
pars.add(tokenPar.build());
//add head end
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.why.greenhouse"))
.paths(PathSelectors.any())
.build()
.globalOperationParameters(pars);
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("GREEN TOKEN SERVER API information")
.description("GREEN TOKEN SERVER API information")
.version("1.0")
.build();
}
}
四、代碼演示及相關(guān)問題修改
這個(gè)時(shí)候我們?cè)L問http://localhost:15030/swagger-ui.html時(shí)出現(xiàn)如下情況:
我們的接口沒有了贼急,再看看請(qǐng)求
和后臺(tái)日志
顯然是我們請(qǐng)求swagger頁(yè)面的時(shí)候有的頁(yè)面被攔截器攔截住了茅茂,通過(guò)請(qǐng)求地址可以看出是在/swagger-resources/configuration請(qǐng)求后跳轉(zhuǎn)到我們攔截之后的接口的,因此對(duì)application.yml文件的配置做了如下修改
其中:
- /swagger-resources/**==>anon
改成
- /swagger-resources/**/**==>anon
修改之后重新啟動(dòng)項(xiàng)目
我們的接口出來(lái)了竿裂,
-
/user/queryAll/**==>anon調(diào)用不需要驗(yàn)證的接口:
可以查到內(nèi)容玉吁,再查看需要登錄的接口:
后臺(tái)打印
的點(diǎn)點(diǎn)滴滴多多多
說(shuō)明進(jìn)入了我們的攔截指定的接口
五、結(jié)束
本項(xiàng)目完全為測(cè)試所用腻异,接口沒有實(shí)際意義进副,本文只是對(duì)shiro Filter做最基本的簡(jiǎn)單的登錄功能驗(yàn)證。踩完后面的坑再寫個(gè)稍微深入一點(diǎn)的悔常。