oauth2demo实现(springboot+mybatis+redis)(代码较多,比较完整,仅供参考)
一、新建springboot项目
<?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.3.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.yuan</groupId> <artifactId>oauth2demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>oauth2demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR1</spring-cloud.version> <fastjson>1.2.54</fastjson> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </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>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <artifactId>spring-boot-autoconfigure</artifactId> <groupId>org.springframework.boot</groupId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <artifactId>spring-cloud-starter-oauth2</artifactId> <groupId>org.springframework.cloud</groupId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.0.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper</artifactId> <version>4.1.5</version> </dependency> <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.22.0-GA</version> </dependency> <dependency> <artifactId>springfox-swagger2</artifactId> <exclusions> <exclusion> <artifactId>swagger-annotations</artifactId> <groupId>io.swagger</groupId> </exclusion> <exclusion> <artifactId>swagger-models</artifactId> <groupId>io.swagger</groupId> </exclusion> </exclusions> <groupId>io.springfox</groupId> <version>2.9.2</version> </dependency> <dependency> <artifactId>springfox-swagger-ui</artifactId> <groupId>io.springfox</groupId> <version>2.9.2</version> </dependency> <dependency> <artifactId>swagger-annotations</artifactId> <groupId>io.swagger</groupId> <version>1.5.21</version> </dependency> <dependency> <artifactId>swagger-models</artifactId> <groupId>io.swagger</groupId> <version>1.5.21</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>${fastjson}</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.8.1</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <dependencyManagement> <dependencies> <dependency> <artifactId>spring-cloud-dependencies</artifactId> <groupId>org.springframework.cloud</groupId> <scope>import</scope> <type>pom</type> <version>${spring-cloud.version}</version> </dependency> </dependencies> </dependencyManagement> </project>
二、resources下添加配置文件
spring:
application:
name: oauth2demo
##解决jvm使用的冲突,tomcat中不同的server 注册jmx必须使用不同的jvm
jmx:
default-domain: oauth2demo
profiles:
active: dev
authorization:
client-id: yuan
client-secret: yuan_secret
validity-hour: 4
## mybatis配置
mybatis:
mapper-locations: classpath:mapper/**/*.xml
type-aliases-package: com.yuan.oauth2demo.model
## 服务监听的端口号
server:
port: 8199
tomcat:
uri-encoding: utf-8
logging:
level:
root: info
com.yuan.oauth2demo.mapper: debug
resource:
server:
configuration:
permitted:
- /v2/api-docs
- /swagger-resources/**
- /swagger-ui.html
- /oauth/*
authenticated:
- /**/**
##api文档配置
swagger:
config:
enable: true
path: com.yuan.oauth2demo
host: 127.0.0.1:8199
token-required: false
api:
title: oauth2demo服务Api文档
terms-of-service-url: http://127.0.0.1:8199/
oatu2demo:
redis:
enable: true
<?xml version="1.0" encoding="UTF-8"?><!-- configuration 中设置debug=true,在springboot启动前会产生辣鸡日志 --> <configuration> <property name="LOG_PATH" value="oauth2demo" /> <!--输出到控制台--> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%red(%d{yyyy-MM-dd HH:mm:ss.SSS}) %highlight(%-6level) %green([ %-25thread ]) %boldMagenta(%-50logger ->%5L) : %cyan(%msg) %n </pattern> <charset>UTF-8</charset> </encoder> </appender> <!-- 全部级别的日志--> <!-- 按照每天生成日志文件 --> <appender name="ALL" class="ch.qos.logback.core.rolling.RollingFileAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">> <!-- 格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 --> <!--<pattern>%red(%d{yyyy-MM-dd HH:mm:ss.SSS}) %highlight(%-5level) %green([ %thread ]) %boldMagenta(%-50logger ->%5L) : %cyan(%msg) %n</pattern>--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-10level [ %thread ] %-50logger ->%5L : %msg %n </pattern> <charset>UTF-8</charset> </encoder> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 日志输出文件名定义 --> <fileNamePattern>./logBack/${LOG_PATH}/all/%d{yyyy-MM-dd}_all.log</fileNamePattern> <!-- 文件保留天数 --> <MaxHistory>30</MaxHistory> </rollingPolicy> <!-- 文件最大大小 --> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <MaxFileSize>10MB</MaxFileSize> </triggeringPolicy> </appender> <!--error 级别的日志--> <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender"> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">> <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--> <!--<pattern>%red(%d{yyyy-MM-dd HH:mm:ss.SSS}) %highlight(%-5level) %green([ %thread ]) %boldMagenta(%-50logger ->%5L) : %cyan(%msg) %n</pattern>--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [ %thread ] %-50logger ->%5L : %msg %n </pattern> <charset>UTF-8</charset> </encoder> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 日志输出文件名定义 --> <fileNamePattern>./logBack/${LOG_PATH}/error/%d{yyyy-MM-dd}_error.log</fileNamePattern> <!-- 文件保留天数 --> <MaxHistory>30</MaxHistory> </rollingPolicy> <!-- 文件最大大小 --> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <MaxFileSize>10MB</MaxFileSize> </triggeringPolicy> </appender> <!-- 输出原始sql --> <logger name="java.sql" level="DEBUG" /> <logger name="org.apache.ibatis" level="DEBUG" /> <logger name="org.springframework" level="warn" /> <logger name="ch.qos.logback" level="DEBUG" /> <!-- 关闭netflix 远程心跳日志 -->、 <logger name="com.netflix.discovery.shared.resolver.aws.ConfigClusterResolver" level="OFF" /> <root level="INFO"> <appender-ref ref="CONSOLE" /> <appender-ref ref="ALL" /> <appender-ref ref="ERROR" /> </root> </configuration>
spring: # redis configuration redis: host: xxx lettuce: pool: max-active: 100 max-idle: 30 max-wait: -1ms min-idle: 5 shutdown-timeout: 200 password: Vdaifu port: 6379 timeout: 6000 database: 1 ##数据源配置 ${server.ip} useUnicode=true&characterEncoding=UTF-8&useTimezone=true&serverTimezone=GMT%2B8 datasource: url: jdbc:mysql://xxx:3306/xxx?useSSL=false&useUnicode=true&characterEncoding=utf-8&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai username: xxx password: xxx driver-class-name: com.mysql.cj.jdbc.Driver
三、common及utils

package com.yuan.oauth2demo.common.encrypt; public enum EncryptionPrefix { /** * 枚举常见的加密算法的前缀 */ BCRYPT("bcrypt", "{bcrypt}"), MD5("MD5", "{MD5}"), NOOP("noop", "{noop}"), SHA_256("SHA-256", "{SHA-256}"); public final String name; public final String prefix; EncryptionPrefix(String name, String prefix) { this.name = name; this.prefix = prefix; } }
package com.yuan.oauth2demo.common.exception; @SuppressWarnings("serial") public class AesException extends Exception { public final static int OK = 0; public final static int VALIDATE_SIGNATURE_ERROR = -40001; public final static int PARSE_XML_ERROR = -40002; public final static int COMPUTE_SIGNATURE_ERROR = -40003; public final static int ILLEGAL_AES_KEY = -40004; public final static int VALIDATE_APPID_ERROR = -40005; public final static int ENCRYPT_AES_ERROR = -40006; public final static int DECRYPT_AES_ERROR = -40007; public final static int ILLEGAL_BUFFER = -40008; private int code; public AesException(int code) { super(getMessage(code)); this.code = code; } private static String getMessage(int code) { switch (code) { case VALIDATE_SIGNATURE_ERROR: return "签名验证错误"; case PARSE_XML_ERROR: return "xml解析失败"; case COMPUTE_SIGNATURE_ERROR: return "sha加密生成签名失败"; case ILLEGAL_AES_KEY: return "SymmetricKey非法"; case VALIDATE_APPID_ERROR: return "appid校验失败"; case ENCRYPT_AES_ERROR: return "aes加密失败"; case DECRYPT_AES_ERROR: return "aes解密失败"; case ILLEGAL_BUFFER: return "解密后得到的buffer非法"; default: // cannot be return null; } } public int getCode() { return code; } }
package com.yuan.oauth2demo.common.exception; import java.io.FileNotFoundException; import java.io.IOException; import java.net.ProtocolException; import java.nio.file.AccessDeniedException; import java.sql.SQLException; public enum ExceptionCodes { //RuntimeException 类型异常 RUNTIME_EXCEPTION("运行时异常", RuntimeException.class), NULL_POINT_EXCEPTION("空指针异常", NullPointerException.class), ARITHMETIC_EXCEPTION("数学错误,被0除", ArithmeticException.class), INDEX_OUT_OF_BOUNDS_EXCEPTION("当某对象的索引超出范围时抛出异常", IndexOutOfBoundsException.class), ARRAY_INDEX_OUT_OF_EXCEPTION("数组下标越界", ArrayIndexOutOfBoundsException.class), CLASS_CAST_EXCEPTION("强制转换异常", ClassCastException.class), ILLEGAL_ARGUMENT_EXCEPTION("非法转换", IllegalArgumentException.class), NUMBER_FORMAT_EXCEPTION("字符串转换为数字异常类", NumberFormatException.class), PROTOCOL_EXCEPTION("网络协议有错误", ProtocolException.class), //IOException 类型异常 IO_EXCEPTION("IO 流异常", IOException.class), FILE_NOT_FOUND_EXCEPTION("文件找不到", FileNotFoundException.class), //数据库 sql 操作异常 SQL_EXCEPTION("操作数据库异常", SQLException.class), //其他相关异常 REFLECTIVE_OPERATION_EXCEPTION("", ReflectiveOperationException.class), ACESS_REFUSED_EXCEPTION("没有权限访问!", AccessDeniedException.class), ILLEGAL_ACESS_EXCEPTION("访问某类被拒绝时抛出的异常", IllegalAccessException.class), // 定制化异常 如微信三方业务异常 //WE_CHAT_EXCEPTION(40001, "微信三方异常", WeChatE) ; private Integer code; private String msg; private Class<? extends Exception> klass; ExceptionCodes(String msg, Class<? extends Exception> klass) { this.msg = msg; this.klass = klass; } ExceptionCodes(Integer code, String msg, Class<? extends Exception> klass) { this.code = code; this.msg = msg; this.klass = klass; } public Integer getCode() { return code; } public String getMsg() { return this.msg; } public Class<? extends Exception> getKlass() { return this.klass; } /** * 根据异常类获取异常信息 **/ public static Integer getCodeByKlass(Class<? extends Exception> klass) { for (ExceptionCodes exceptionCodes : ExceptionCodes.values()) { if (exceptionCodes.getKlass() == klass) { return exceptionCodes.getCode(); } } return null; } /** * 根据异常类获取异常信息 **/ public static String getMsgByKlass(Class<? extends Exception> klass) { for (ExceptionCodes exceptionCodes : ExceptionCodes.values()) { if (exceptionCodes.getKlass() == klass) { return exceptionCodes.getMsg(); } } return null; } }
package com.yuan.oauth2demo.common.exception; import java.io.Serializable; public class ServiceException extends RuntimeException implements Serializable { private static final long SERIALIZABLE_UID = 1L; private int code; private String msg; public ServiceException() { super(); } public ServiceException(String msg) { this.msg = msg; } public ServiceException(int code, String msg) { this.code = code; this.msg = msg; } public ServiceException(String message, int code, String msg) { super(message); this.code = code; this.msg = msg; } public static long getSerializableUid() { return SERIALIZABLE_UID; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } //------------------------------------------ /** * 业务枚举 **/ public enum Problems { /** * 服务异常 */ EXCEPTION(3000, "内部服务异常"), /** * 小程序登录授权失败异常 */ SMALL_LOGIN_AUTHORIZE_FAIL_EXCEPTION(400003, "登录授权失败"), /** * 小程序授权过期 */ AUTHORIZE_EXPIRE_EXCEPTION_CODE(400002, "授权过期"), /** * 用户没有绑定手机号码 */ USER_NOT_BIND_MOBILE_EXCEPTION_CODE(400001, "微信用户未绑定手机号码"), /** * 请求太频繁 */ FREQUENT_REQUESTS_EXCEPTION(400006, "请求太频繁"), /** * 提现失败 */ CASH_WITHDRAWAL_EXCEPTION(400007, "提现失败"); int code; String msg; Problems(int code, String msg) { this.code = code; this.msg = msg; } public int getCode() { return this.code; } public String getMsg() { return this.msg; } } }
package com.yuan.oauth2demo.common.exception; import org.springframework.security.access.AccessDeniedException; import javax.websocket.DecodeException; import java.io.FileNotFoundException; import java.io.IOException; import java.net.ProtocolException; import java.sql.SQLException; public enum SystemExceptionCodes { //RuntimeException 类型异常 RUNTIME_EXCEPTION("运行时异常", RuntimeException.class), NULL_POINT_EXCEPTION("空指针异常", NullPointerException.class), ARITHMETIC_EXCEPTION("数学错误,被0除", ArithmeticException.class), INDEX_OUT_OF_BOUNDS_EXCEPTION("当某对象的索引超出范围时抛出异常", IndexOutOfBoundsException.class), ARRAY_INDEX_OUT_OF_EXCEPTION("数组下标越界", ArrayIndexOutOfBoundsException.class), CLASS_CAST_EXCEPTION("强制转换异常", ClassCastException.class), ILLEGAL_ARGUMENT_EXCEPTION("非法转换", IllegalArgumentException.class), NUMBER_FORMAT_EXCEPTION("字符串转换为数字异常类", NumberFormatException.class), PROTOCOL_EXCEPTION("网络协议有错误", ProtocolException.class), //IOException 类型异常 IO_EXCEPTION("IO 流异常", IOException.class), FILE_NOT_FOUND_EXCEPTION("文件找不到", FileNotFoundException.class), //数据库 sql 操作异常 SQL_EXCEPTION("操作数据库异常", SQLException.class), //其他相关异常 REFLECTIVE_OPERATION_EXCEPTION("", ReflectiveOperationException.class), ACESS_REFUSED_EXCEPTION("没有权限访问!", AccessDeniedException.class), DECODE_EXCEPTION("feign 客户端之间的请求响应类型转换异常", DecodeException.class), ILLEGAL_ACESS_EXCEPTION("访问某类被拒绝时抛出的异常", IllegalAccessException.class), // 定制化异常 如微信三方业务异常 //WE_CHAT_EXCEPTION(40001, "微信三方异常", WeChatE) ; private Integer code; private String msg; private Class<? extends Exception> klass; SystemExceptionCodes(String msg, Class<? extends Exception> klass) { this.msg = msg; this.klass = klass; } SystemExceptionCodes(Integer code, String msg, Class<? extends Exception> klass) { this.code = code; this.msg = msg; this.klass = klass; } /** * 根据异常类获取异常信息 **/ public static Integer getCodeByKlass(Class<? extends Exception> klass) { for (SystemExceptionCodes systemExceptionCodes : SystemExceptionCodes.values()) { if (systemExceptionCodes.getKlass() == klass) { return systemExceptionCodes.getCode(); } } return null; } /** * 根据异常类获取异常信息 **/ public static String getMsgByKlass(Class<? extends Exception> klass) { for (SystemExceptionCodes systemExceptionCodes : SystemExceptionCodes.values()) { if (systemExceptionCodes.getKlass() == klass) { return systemExceptionCodes.getMsg(); } } return null; } public Integer getCode() { return code; } public String getMsg() { return this.msg; } public Class<? extends Exception> getKlass() { return this.klass; } }
package com.yuan.oauth2demo.common.http; public enum HttpStatus { /** * http 状态码枚举 */ CONTINUE(100, "Continue", "请继续发送请求的剩余部分"), SWITCHING_PROTOCOLS(101, "Switching Protocols", "协议切换"), PROCESSING(102, "Processing", "请求将继续执行"), // for 103 https://news.ycombinator.com/item?id=15590049 CHECKPOINT(103, "Checkpoint", "可以预加载"), OK(200, "OK", "请求已经成功处理"), CREATED(201, "Created", "请求已经成功处理,并创建了资源"), ACCEPTED(202, "Accepted", "请求已经接受,等待执行"), NON_AUTHORITATIVE_INFORMATION(203, "Non-Authoritative Information", "请求已经成功处理,但是信息不是原始的"), NO_CONTENT(204, "No Content", "请求已经成功处理,没有内容需要返回"), RESET_CONTENT(205, "Reset Content", "请求已经成功处理,请重置视图"), PARTIAL_CONTENT(206, "Partial Content", "部分Get请求已经成功处理"), MULTI_STATUS(207, "Multi-Status", "请求已经成功处理,将返回XML消息体"), ALREADY_REPORTED(208, "Already Reported", "请求已经成功处理,一个DAV的绑定成员被前一个请求枚举,并且没有被再一次包括"), IM_USED(226, "IM Used", "请求已经成功处理,将响应一个或者多个实例"), MULTIPLE_CHOICES(300, "Multiple Choices", "提供可供选择的回馈"), MOVED_PERMANENTLY(301, "Moved Permanently", "请求的资源已经永久转移"), FOUND(302, "Found", "请重新发送请求"), // MOVED_TEMPORARILY(302, "Moved Temporarily", "") 已经过时 SEE_OTHER(303, "See Other", "请以Get方式请求另一个URI"), NOT_MODIFIED(304, "Not Modified", "资源未改变"), USE_PROXY(305, "Use Proxy", "请通过Location域中的代理进行访问"), // 306在新版本的规范中被弃用 TEMPORARY_REDIRECT(307, "Temporary Redirect", "请求的资源临时从不同的URI响应请求"), RESUME_INCOMPLETE(308, "Resume Incomplete", "请求的资源已经永久转移"), BAD_REQUEST(400, "Bad Request", "请求错误,请修正请求"), UNAUTHORIZED(401, "Unauthorized", "没有被授权或者授权已经失效"), PAYMENT_REQUIRED(402, "Payment Required", "预留状态"), FORBIDDEN(403, "Forbidden", "请求被理解,但是拒绝执行"), NOT_FOUND(404, "Not Found", "资源未找到"), METHOD_NOT_ALLOWED(405, "Method Not Allowed", "请求方法不允许被执行"), NOT_ACCEPTABLE(406, "Not Acceptable", "请求的资源不满足请求者要求"), PROXY_AUTHENTICATION_REQUIRED(407, "Proxy Authentication Required", "请通过代理进行身份验证"), REQUEST_TIMEOUT(408, "Request Timeout", "请求超时"), CONFLICT(409, "Conflict", "请求冲突"), GONE(410, "Gone", "请求的资源不可用"), LENGTH_REQUIRED(411, "Length Required", "Content-Length未定义"), PRECONDITION_FAILED(412, "Precondition Failed", "不满足请求的先决条件"), REQUEST_ENTITY_TOO_LARGE(413, "Request Entity Too Large", "请求发送的实体太大"), REQUEST_URI_TOO_LONG(414, "Request-URI Too Long", "请求的URI超长"), UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type", "请求发送的实体类型不受支持"), REQUESTED_RANGE_NOT_SATISFIABLE(416, "Requested range not satisfiable", "Range" + "指定的范围与当前资源可用范围不一致"), EXPECTATION_FAILED(417, "Expectation Failed", "请求头Expect中指定的预期内容无法被服务器满足"), // I_AM_A_TEAPOT(418, "I'm a teapot", ""), 该代码没有被服务器实现 // INSUFFICIENT_SPACE_ON_RESOURCE(419, "Insufficient Space On Resource", ""), 已经过时 // METHOD_FAILURE(420, "Method Failure", ""), 已经过时 // DESTINATION_LOCKED(421, "Destination Locked", ""), 已经过时 UNPROCESSABLE_ENTITY(422, "Unprocessable Entity", "请求格式正确,但是由于含有语义错误,无法响应"), LOCKED(423, "Locked", "当前资源被锁定"), FAILED_DEPENDENCY(424, "Failed Dependency", "由于之前的请求发生错误,导致当前请求失败"), UPGRADE_REQUIRED(426, "Upgrade Required", "客户端需要切换到TLS1.0"), PRECONDITION_REQUIRED(428, "Precondition Required", "请求需要提供前置条件"), TOO_MANY_REQUESTS(429, "Too Many Requests", "请求过多"), REQUEST_HEADER_FIELDS_TOO_LARGE(431, "Request Header Fields Too Large", "请求头超大,拒绝请求"), INTERNAL_SERVER_ERROR(500, "Internal Server Error", "服务器内部错误"), NOT_IMPLEMENTED(501, "Not Implemented", "服务器不支持当前请求的部分功能"), BAD_GATEWAY(502, "Bad Gateway", "响应无效"), SERVICE_UNAVAILABLE(503, "Service Unavailable", "服务器维护或者过载,拒绝服务"), GATEWAY_TIMEOUT(504, "Gateway Timeout", "上游服务器超时"), HTTP_VERSION_NOT_SUPPORTED(505, "HTTP Version not supported", "不支持的HTTP版本"), VARIANT_ALSO_NEGOTIATES(506, "Variant Also Negotiates", "服务器内部配置错误"), INSUFFICIENT_STORAGE(507, "Insufficient Storage", "服务器无法完成存储请求所需的内容"), LOOP_DETECTED(508, "Loop Detected", "服务器处理请求时发现死循环"), BANDWIDTH_LIMIT_EXCEEDED(509, "Bandwidth Limit Exceeded", "服务器达到带宽限制"), NOT_EXTENDED(510, "Not Extended", "获取资源所需的策略没有被满足"), NETWORK_AUTHENTICATION_REQUIRED(511, "Network Authentication Required", "需要进行网络授权"); /** * 枚举服务器接口响应的多种状态 */ private static final int INFORMATIONAL = 1, SUCCESSFUL = 2, REDIRECTION = 3, CLIENT_ERROR = 4, SERVER_ERROR = 5; /** * 状态码 */ private final int code; /** * 美式解释短语 */ private final String reasonPhraseUs; /** * 中文解释短语 */ private final String reasonPhraseCn; HttpStatus(int code, String reasonPhraseUs, String reasonPhraseCn) { this.code = code; this.reasonPhraseUs = reasonPhraseUs; this.reasonPhraseCn = reasonPhraseCn; } public static HttpStatus valueOf(int code) { for (HttpStatus httpStatus : values()) { if (httpStatus.code() == code) { return httpStatus; } } throw new IllegalArgumentException("No matching constant for [" + code + "]"); } public int code() { return code; } public String reasonPhraseUs() { return reasonPhraseUs; } public String reasonPhraseCn() { return reasonPhraseCn; } public boolean isInformational() { return type() == INFORMATIONAL; } public boolean isSuccessful() { return type() == SUCCESSFUL; } public boolean isRedirection() { return type() == REDIRECTION; } public boolean isClientError() { return type() == CLIENT_ERROR; } public boolean isServerError() { return type() == SERVER_ERROR; } private int type() { return code / 100; } }
package com.yuan.oauth2demo.common.http; import org.springframework.http.HttpStatus; public class ResponseResult<T> { private int code; private T data; private String msg; public ResponseResult() { } public ResponseResult(int code, String msg) { this.code = code; this.msg = msg; } public ResponseResult(int code, T data, String msg) { this.code = code; this.data = data; this.msg = msg; } public static <T> ResponseResult<T> build(T data) { ResponseResult<T> result = new ResponseResult<T>(); result.setData(data); return result; } public ResponseResult<T> success() { this.code = HttpStatus.OK.value(); this.msg = HttpStatus.OK.getReasonPhrase(); return this; } public ResponseResult<T> fail() { this.code = 3000; this.msg = "操作失败!"; return this; } public ResponseResult<T> empty() { this.code = HttpStatus.OK.value(); this.data = null; this.msg = "查询到的结果为空!"; return this; } public ResponseResult<T> success(T data) { this.code = HttpStatus.OK.value(); this.data = data; this.msg = HttpStatus.OK.getReasonPhrase(); return this; } public ResponseResult<T> fail(String msg) { this.code = 3000; this.msg = msg; return this; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public T getData() { return data; } public void setData(T data) { this.data = data; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }

package com.yuan.oauth2demo.utils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.Timestamp; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.Instant; import java.time.LocalDate; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.Calendar; import java.util.Date; import java.util.Objects; public class DateUtils { /** * 日期格式,年份,例如:2004,2008 */ public static final String DATE_FORMAT_YYYY = "yyyy"; // ==格式到年== /** * 日期格式,年份和月份,例如:200707,200808 */ public static final String DATE_FORMAT_YYYYMM = "yyyyMM"; // ==格式到年月 == /** * 日期格式,年份和月份,例如:200707,2008-08 */ public static final String DATE_FORMAT_YYYY_MM = "yyyy-MM"; /** * 日期格式,年月日,例如:050630,080808 */ public static final String DATE_FORMAT_YYMMDD = "yyMMdd"; // ==格式到年月日== /** * 日期格式,年月日,用横杠分开,例如:06-12-25,08-08-08 */ public static final String DATE_FORMAT_YY_MM_DD = "yy-MM-dd"; /** * 日期格式,年月日,例如:20050630,20080808 */ public static final String DATE_FORMAT_YYYYMMDD = "yyyyMMdd"; /** * 日期格式,年月日,用横杠分开,例如:2006-12-25,2008-08-08 */ public static final String DATE_FORMAT_YYYY_MM_DD = "yyyy-MM-dd"; /** * 日期格式,年月日,例如:2016.10.05 */ public static final String POINT_YYYY_MMU_DD = "yyyy.MM.dd"; /** * 日期格式,月日,例如:10.05 */ public static final String POINT_MMU_DD = "MM.dd"; /** * 日期格式,月 例如:10 */ public static final String POINT_MMU = "MM"; /** * 日期格式,年月日,例如:2016年10月05日 */ public static final String CHINA_YYYY_MMU_DD = "yyyy年MM月dd日"; /** * 日期格式,年月日,例如:2016年10月05日 12:00 */ public static final String CHINA_YYYYMMDD_HHU_MM = "yyyy年MM月dd日 HH:mm"; /** * 日期格式,年月日,例如:2016100512:00:99 */ public static final String YYYY_MMU_DD_HHU_MM_SS1 = "yyyyMMddHH:mm:ss"; /** * 日期格式,年月日时分,例如:200506301210,200808081210 */ public static final String YYYY_MMU_DD_HHU_MM = "yyyyMMddHHmm"; // ==格式到年月日 时分 == /** * 日期格式,年月日时分,例如:20001230 12:00,20080808 20:08 */ public static final String YYYY_MMU_DD__HHU_MM1 = "yyyyMMdd HH:mm"; /** * 日期格式,年月日时分,例如:2000-12-30 12:00,2008-08-08 20:08 */ public static final String YYYY_MMU_DD__HHU_MM2 = "yyyy-MM-dd HH:mm"; /** * 日期格式,年月日时分秒,例如:20001230120000,20080808200808 */ public static final String YYYY_MMU_DD_HHU_MM_SS2 = "yyyyMMddHHmmss"; // ==格式到年月日 时分秒== /** * 日期格式,年月日时分秒,年月日用横杠分开,时分秒用冒号分开 * 例如:2005-05-10 23:20:00,2008-08-08 20:08:08 */ public static final String YYYY_MMU_DD__HHU_MM_SS = "yyyy-MM-dd HH:mm:ss"; /** * 日期格式,年月日时分秒毫秒,例如:20001230120000123,20080808200808456 */ public static final String YYYY_MMU_DD_HHU_MM_SS_SSSU = "yyyyMMddHHmmssSSS"; // ==格式到年月日 时分秒 毫秒== /** * 日期格式,月日时分,例如:10-05 12:00 */ public static final String MMU_DD__HHU_MM = "MM-dd HH:mm"; // ==特殊格式== /** * 日期格式,天/月 ,比如 :四月十二号 : 12/4 */ public static final String D_M = "d/M"; /** * 时间格式 小时:分钟 ,比如 十三点十五分 :13:15 */ public static final String HHU_MM = "HH:mm"; public final static DateTimeFormatter FORMAT = DateTimeFormatter.ofPattern(DateUtils.CHINA_YYYYMMDD_HHU_MM); public final static DateTimeFormatter FORMAT_112 = DateTimeFormatter.ofPattern(DateUtils.YYYY_MMU_DD__HHU_MM_SS); public final static DateTimeFormatter FORMAT_113 = DateTimeFormatter.ofPattern(DateUtils.YYYY_MMU_DD_HHU_MM_SS1); public final static DateTimeFormatter FORMAT_114 = DateTimeFormatter.ofPattern(DateUtils.DATE_FORMAT_YYYY_MM_DD); private static final Logger logger = LoggerFactory.getLogger(DateUtils.class); /* ************工具方法*************** */ /** * 格式化Date时间 * * @param time Date类型时间 * @param pattern String类型格式 * @param defaultValue 默认值为当前时间Date * @return 格式化后的字符串 */ public static String format(ZonedDateTime time, String pattern, final ZonedDateTime defaultValue) { try { return format(time, pattern); } catch (Exception e) { if (defaultValue != null) { return format(defaultValue, pattern); } else { return format(ZonedDateTime.now(), pattern); } } } /** * 格式化Date时间 * * @param time Date类型时间 * @param timeFormat String类型格式 * @param defaultValue 默认时间值String类型 * @return 格式化后的字符串 */ public static String format(ZonedDateTime time, String timeFormat, final String defaultValue) { try { DateTimeFormatter formatter = DateTimeFormatter.ofPattern(timeFormat); return formatter.format(time); } catch (Exception e) { return defaultValue; } } /** * 格式化时间戳{@see Timestamp} 如果格式化抛出异常将会返回空串 * * @param timestamp 时间戳 * @param timeFormat 格式化模板 * @return 返回一个格式化后的字符串 * @see Timestamp */ public static String format(Timestamp timestamp, String timeFormat) { return format(timestamp, timeFormat, StringUtils.EMPTY); } public static String format(Timestamp timestamp, String timeFormat, String defaultValue) { if (timestamp == null || StringUtils.isEmpty(timeFormat)) { return StringUtils.EMPTY; } ZoneId zone = ZoneId.systemDefault(); ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(timestamp.toInstant(), zone); return format(zonedDateTime, timeFormat, defaultValue); } /** * 格式化String时间 * * @param time String类型时间 * @param formatPattern String类型格式 * @return 格式化后的Date日期 */ public static ZonedDateTime parse(String time, String formatPattern) { if (StringUtils.isEmpty(time)) { return null; } DateTimeFormatter deteFormatter = DateTimeFormatter.ofPattern(formatPattern); //需要确认时区 因为使用的 ZonedDateTime 时区信息从ZoneId中拿到 return LocalDate.parse(time, deteFormatter).atStartOfDay(ZoneId.systemDefault()); } /** * 格式化String时间 * * @param strTime String类型时间 * @param formatPattern String类型格式 * @param defaultValue 异常时返回的默认值 * @return 返回格式化时间 */ public static ZonedDateTime parse(String strTime, String formatPattern, ZonedDateTime defaultValue) { try { return parse(strTime, formatPattern); } catch (Exception e) { return defaultValue; } } /** * 格式化日期对象 通过 formatPattern * * @param date 日期对象 * @param formatPattern 格式化模板 * @return 返回格式化结果 如果格式化抛出异常将会返回空串 * @see StringUtils * {@link #format(ZonedDateTime, String, String)} */ public static String format(ZonedDateTime date, String formatPattern) { return format(date, formatPattern, StringUtils.EMPTY); } /** * 格式化日期对象 通过 formatPattern * * @param date 日期对象 * @param dateFormat 格式化日期的工具类 * @return 返回格式化结果 */ public static String format(ZonedDateTime date, DateTimeFormatter dateFormat) { return dateFormat.format(date); } public static String format(String target, String fromPattern, String toPattern) { if (StringUtils.isEmpty(target) || StringUtils.isEmpty(fromPattern) || StringUtils.isEmpty(toPattern)) { logger.error("method signature: format(String target, String fromPattern, String " + "toPattern) :format 字符串=" + target + "失败!!返回原值 cause:入参有空值 目标字符串 = " + target + " fromPattern = " + fromPattern + " toPattern = " + toPattern); return target; } ZonedDateTime dateTime = parse(target, fromPattern); return format(dateTime, toPattern); } public static String formatDate(Date date,String formatPattern){ SimpleDateFormat simpleDateFormat = new SimpleDateFormat(formatPattern); return simpleDateFormat.format(date); } public static Calendar getCalendar(Date date) { if (date == null) { return null; } DateFormat df = DateFormat.getDateInstance(); df.format(date); return df.getCalendar(); } /** * 获取时间的小时 * * @param date * @return */ public static Integer getHour(Date date) { if (date == null) { return null; } return getCalendar(date).get(Calendar.HOUR_OF_DAY); } /** * @Description: date转为string * @Author: Paul * @Date: 2019/12/4/004 16:32 */ public static String parseToString(Date date, String style) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat(); simpleDateFormat.applyPattern(style); String str; if (date == null) { return null; } else { str = simpleDateFormat.format(date); return str; } } public static Date parseToDate(String s, String style) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat(); simpleDateFormat.applyPattern(style); Date date; if (s == null || s.length() < 8) { return null; } try { date = simpleDateFormat.parse(s); } catch (ParseException e) { e.printStackTrace(); return null; } return date; } /** * 判断输入的日期是否在下周区间 * * @return false:不是下周;true:下周 * @author nemowang */ public static boolean isNextWeek(Date date) { // 设置时间格式 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Calendar calendar1 = Calendar.getInstance(); Calendar calendar2 = Calendar.getInstance(); int dayOfWeek = calendar1.get(Calendar.DAY_OF_WEEK) - 1; int offset1 = 1 - dayOfWeek; int offset2 = 7 - dayOfWeek; calendar1.add(Calendar.DATE, offset1 + 7); calendar2.add(Calendar.DATE, offset2 + 7); // 下周星期一 String monday = sdf.format(calendar1.getTime()); //System.out.println(monday); // 下周星期日 String sunday = sdf.format(calendar2.getTime()); //System.out.println(sunday); String format = sdf.format(date); try { Date parse = sdf.parse(format); Date mon = sdf.parse(monday); Date sun = sdf.parse(sunday); return parse.compareTo(mon) >= 0 && sun.compareTo(parse) >= 0; } catch (ParseException e) { e.printStackTrace(); } return false; } /** * 计算年龄,根据生日日期 * * @param birDate 生日 * @return 年龄 */ public static String calculateAge(Date birDate) { LocalDate today = LocalDate.now(); Instant instant = birDate.toInstant(); ZoneId zoneId = ZoneId.systemDefault(); // atZone()方法返回在指定时区从此Instant生成的ZonedDateTime。 LocalDate localDate = instant.atZone(zoneId).toLocalDate(); return String.valueOf(ChronoUnit.YEARS.between(localDate, today)); } /** * 计算年龄,根据生日日期 * * @param birDateStr 生日日期字符串 * @param pattern use in format {@code birDateStr} * @return 年龄 */ public static String calculateAge(String birDateStr, String pattern) { LocalDate today = LocalDate.now(); ZonedDateTime zonedDateTime = parse(birDateStr, pattern); return String.valueOf(ChronoUnit.YEARS.between(Objects.requireNonNull(zonedDateTime).toLocalDate(), today)); } /** * 获取星期几 通过日期 * * @param date 日期 * @param prefix 前缀:周,星期 * @return 返回星期几 example :星期一 */ public static String getWeekXQ(Date date, String prefix) { Calendar calendar = Calendar.getInstance(); calendar.setTime(date); int day = calendar.get(Calendar.DAY_OF_WEEK); return getWeekXQ(day, prefix); } private static String getWeekXQ(int dayOfWeek, String prefix) { String week = prefix; switch (dayOfWeek) { case 2: week += "一"; break; case 3: week += "二"; break; case 4: week += "三"; break; case 5: week += "四"; break; case 6: week += "五"; break; case 7: week += "六"; break; case 1: week += "日"; break; default: break; } return week; } /** * 获取星期几 通过日期 * * @param dateStr 日期字符串 * @param prefix 前缀:周,星期 * @return 返回星期几 example :星期一 */ public static String getWeekXQ(String dateStr, String pattern, String prefix) { Date dateTime; dateTime = parseToDate(dateStr, pattern); return dateTime == null ? StringUtils.EMPTY : getWeekXQ(dateTime, prefix); } /** * 比较 {@code target} 跟{@code subject}日期字符串的大小 * * @param target 目标日期字符 * @param subject 比较主体日期字符串 * @param subPattern {@code target}日期模板 * @param tarPattern {@code subject}日期模板 * @return 如果 {@code target}在{{@code subject}之前则返回true */ public static Boolean isBefore(String target, String tarPattern, String subject, String subPattern) { ZonedDateTime dateTime = parse(target, tarPattern); ZonedDateTime subDateTime = parse(subject, subPattern); return Objects.requireNonNull(dateTime).isBefore(Objects.requireNonNull(subDateTime)); } /** * 比较 {@code target} 跟{@code subject}日期字符串的大小 * * @param target 目标日期字符 * @param subject 比较主体日期字符串 * @param pattern 日期模板 * @return 如果 {@code target}在{{@code subject}之前则返回true */ public static Boolean isBefore(String target, String subject, String pattern) { ZonedDateTime dateTime = parse(target, pattern); ZonedDateTime subDateTime = parse(subject, pattern); return Objects.requireNonNull(dateTime).isBefore(Objects.requireNonNull(subDateTime)); } }
package com.yuan.oauth2demo.utils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; import java.util.HashMap; import java.util.Map; public class RedisHelper { private static final Logger logger = LoggerFactory.getLogger(RedisHelper.class); private RedisConnectionFactory redisConnectionFactory; public RedisHelper(RedisConnectionFactory redisConnectionFactory) { this.redisConnectionFactory = redisConnectionFactory; } /** * 获取redis connection 实例连接 * * @return RedisConnection */ private RedisConnection getConnection() { return this.redisConnectionFactory.getConnection(); } /** * 关闭jedis 实例连接 * * @param connect RedisConnection */ public void closeConnection(RedisConnection connect) { if (null != connect) { connect.close(); } } //--------------------------------------------------基于Hash 数据的操作 public String hget(String key, String field) { RedisConnection conn = null; try { conn = this.getConnection(); byte[] bytes = conn.hGet(key.getBytes(), field.getBytes()); if (bytes != null) { return new String(bytes); } } finally { this.closeConnection(conn); } return StringUtils.EMPTY; } public Boolean hset(String key, String field, String value) { RedisConnection conn = null; try { conn = this.getConnection(); return conn.hSet(key.getBytes(), field.getBytes(), value.getBytes()); } finally { this.closeConnection(conn); } } public void hdel(String key, String field) { RedisConnection conn = null; try { conn = this.getConnection(); conn.hDel(key.getBytes(), field.getBytes()); } finally { this.closeConnection(conn); } } public void hmset(String key, Map<String, String> params) { RedisConnection conn = null; HashMap<byte[], byte[]> map = new HashMap<>(); params.forEach((k, v) -> { map.put(k.getBytes(), v.getBytes()); }); try { conn = this.getConnection(); conn.hMSet(key.getBytes(), map); } finally { this.closeConnection(conn); } } //--------------------------------------------------基于String 数据的操作 public String get(String key) { RedisConnection conn = null; try { conn = this.getConnection(); byte[] bytes = conn.get(key.getBytes()); if (bytes != null) { return new String(bytes); } } finally { this.closeConnection(conn); } return StringUtils.EMPTY; } public void set(String key, String value) { RedisConnection conn = null; try { conn = this.getConnection(); conn.set(key.getBytes(), value.getBytes()); } finally { this.closeConnection(conn); } } // key 过期方面的设置 public void expire(String key, int second) { RedisConnection conn = null; try { conn = this.getConnection(); conn.expire(key.getBytes(), second); } finally { this.closeConnection(conn); } } }
四、user表及相关服务
1.建表语句及插入数据语句
create table `user` ( `user_id` varchar (192), `user_name` varchar (135), `user_pwd` varchar (135), `create_time` datetime ); insert into `user` (`user_id`, `user_name`, `user_pwd`, `create_time`) values('1','yuan','1483177275','2020-01-01 00:00:00');
2.model
package com.yuan.oauth2demo.model.system.entity; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import java.util.Date; import javax.persistence.*; import lombok.Data; @ApiModel(value="com-yuan-oauth2demo-model-system-entity-User") @Data @Table(name = "`user`") public class User { /** * 主键id */ @Id @Column(name = "user_id") @ApiModelProperty(value="主键id") private String userId; /** * 用户名 */ @Column(name = "user_name") @ApiModelProperty(value="用户名") private String userName; /** * 密码 */ @Column(name = "user_pwd") @ApiModelProperty(value="密码") private String userPwd; /** * 记录创建日期时间 */ @Column(name = "create_time") @ApiModelProperty(value="记录创建日期时间") private Date createTime; }
package com.yuan.oauth2demo.model.system.bean; import lombok.Data; import java.util.List; @Data public class UserDetails { /** * 用户名 */ private String username; /** * 密码 */ private String pwd; /** * 角色 */ private List<String> roles; }
3.mapper及service
package com.yuan.oauth2demo.mapper.demo.system; import com.yuan.oauth2demo.mapper.BaseMapper; import com.yuan.oauth2demo.model.system.bean.UserDetails; import com.yuan.oauth2demo.model.system.entity.User; import org.apache.ibatis.annotations.Param; public interface UserMapper { UserDetails findUserDetailsByUsername(@Param("username") String username); User findUserByUsername(@Param("username") String username); }
<?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.yuan.oauth2demo.mapper.demo.system.UserMapper"> <resultMap id="BaseResultMap" type="com.yuan.oauth2demo.model.system.entity.User"> <!--@mbg.generated generated on Fri Aug 28 10:48:18 GMT+08:00 2020.--> <!--@Table `user`--> <result column="user_id" jdbcType="VARCHAR" property="userId" /> <result column="user_name" jdbcType="VARCHAR" property="userName" /> <result column="user_pwd" jdbcType="VARCHAR" property="userPwd" /> <result column="create_time" jdbcType="TIMESTAMP" property="createTime" /> </resultMap> <sql id="Base_Column_List"> <!--@mbg.generated generated on Fri Aug 28 10:48:18 GMT+08:00 2020.--> user_id, user_name, user_pwd, create_time </sql> <resultMap id="UserDetailsResultMap" type="com.yuan.oauth2demo.model.system.bean.UserDetails"> <id column="username" property="username" jdbcType="VARCHAR" /> <result column="password" property="pwd" jdbcType="VARCHAR" /> <collection property="roles" ofType="string" javaType="java.util.List"> <constructor> <arg column="role" javaType="java.lang.String" jdbcType="VARCHAR" /> </constructor> </collection> </resultMap> <select id="findUserDetailsByUsername" resultMap="UserDetailsResultMap"> SELECT user_name username,user_pwd password,null role FROM USER <where> <if test="username != null and username != ''"> and user_name = #{username} </if> </where> </select> <select id="findUserByUsername" resultMap="BaseResultMap"> SELECT * FROM USER <where> <if test="username != null and username != ''"> and user_name = #{username} </if> </where> </select> </mapper>
package com.yuan.oauth2demo.service; import com.yuan.oauth2demo.common.http.ResponseResult; public interface UserService { ResponseResult findUserByUsername(String username); }
package com.yuan.oauth2demo.service.impl; import com.yuan.oauth2demo.common.http.ResponseResult; import com.yuan.oauth2demo.mapper.demo.system.UserMapper; import com.yuan.oauth2demo.model.system.entity.User; import com.yuan.oauth2demo.service.UserService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @Service public class UserServiceImpl implements UserService { private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class); private final UserMapper userMapper; public UserServiceImpl(UserMapper userMapper) { this.userMapper = userMapper; } @Override public ResponseResult findUserByUsername(String username) { User user = userMapper.findUserByUsername(username); if(user != null){ return ResponseResult.build(user).success(); } return ResponseResult.build(null).fail(); } }
4.UserDetailServiceImpl(重点)
package com.yuan.oauth2demo.service.impl; import com.yuan.oauth2demo.common.encrypt.EncryptionPrefix; import com.yuan.oauth2demo.mapper.demo.system.UserMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @Service public class UserDetailServiceImpl implements UserDetailsService { private static final Logger logger = LoggerFactory.getLogger(UserDetailServiceImpl.class); private final UserMapper userMapper; private final PasswordEncoder passwordEncoder; public UserDetailServiceImpl(UserMapper userMapper, @Qualifier("bCryptPasswordEncoder")PasswordEncoder passwordEncoder) { this.userMapper = userMapper; this.passwordEncoder = passwordEncoder; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { com.yuan.oauth2demo.model.system.bean.UserDetails userDetail = userMapper.findUserDetailsByUsername(username); if(userDetail == null){ logger.info("用户信息查询为空"); return null; } return new org.springframework.security.core.userdetails.User(userDetail.getUsername(), EncryptionPrefix.BCRYPT.prefix + passwordEncoder.encode(userDetail.getPwd()), AuthorityUtils.createAuthorityList(userDetail.getRoles() == null ? new String[0] : userDetail.getRoles().toArray(new String[0]))); } }
五、conf相关配置类

package com.yuan.oauth2demo.conf; import com.alibaba.fastjson.JSON; import com.yuan.oauth2demo.common.exception.ServiceException; import com.yuan.oauth2demo.common.http.ResponseResult; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.security.core.AuthenticationException; import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component public class AuthExceptionEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) { Throwable cause = authException.getCause(); response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE); try { if (cause instanceof InvalidTokenException) { response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.getWriter() .write(JSON.toJSONString( new ResponseResult<String>(ServiceException.Problems.EXCEPTION.getCode(), "无效的token")) ); } else { response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); response.getWriter() .write(JSON.toJSONString( new ResponseResult<String>(ServiceException.Problems.EXCEPTION.getCode(), "服务器异常")) ); } } catch (IOException e) { e.printStackTrace(); } } }
package com.yuan.oauth2demo.conf; import com.yuan.oauth2demo.common.encrypt.EncryptionPrefix; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; import org.springframework.security.oauth2.provider.ClientDetailsService; import org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService; import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint; import org.springframework.security.oauth2.provider.token.DefaultTokenServices; import org.springframework.security.oauth2.provider.token.TokenEnhancerChain; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; import java.security.Principal; import java.util.Map; import java.util.concurrent.TimeUnit; @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection") @Configuration @EnableAuthorizationServer @ConfigurationProperties(prefix = "spring.authorization") public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter implements InitializingBean { /** * 密码模式授权模式 */ public static final String GRANT_TYPE_PASSWORD = "password"; /** * {@link TokenEndpoint#postAccessToken(Principal, Map)} * 授权token 参数中的用户名 参数key */ public static final String USERNAME = "username"; /** * 支持刷新token */ public static final String REFRESH_TOKEN = "refresh_token"; /** * 授权类型key */ public static final String GRANT_TYPE = "grant_type"; /** * 简化授权模式 */ private static final String IMPLICIT = "implicit"; /** * 客户端凭证模式,支持客户端无需携带账号密码校验,只需要设置固定的 client id and client secret 来校验 */ private static final String CLIENT_CREDENTIALS = "client_credentials"; /** * clientId */ private String clientId; /** * clientSecret */ private String clientSecret; /** * 有效期 unit:小时 */ private int validityHour; public final int getValidityHour() { return validityHour; } public final void setValidityHour(int validityHour) { this.validityHour = validityHour; } public final String getClientId() { return clientId; } public final void setClientId(String clientId) { this.clientId = clientId; } public final String getClientSecret() { return clientSecret; } public final void setClientSecret(String clientSecret) { this.clientSecret = clientSecret; } @Autowired private LettuceConnectionFactory lettuceConnectionFactory; @Autowired @Qualifier("bCryptPasswordEncoder") private PasswordEncoder passwordEncoder; /** * 认证管理器 */ @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection") @Autowired @Qualifier("authenticationManagerBean") private AuthenticationManager authenticationManager; @Autowired private AuthExceptionEntryPoint authExceptionEntryPoint; @Autowired private CustomAccessDeniedHandler accessDeniedHandler; @Autowired private AuthorizationTokenResponseExceptionTranslator exceptionTranslator; private ObjectPostProcessor<Object> objectPostProcessor = new ObjectPostProcessor<Object>() { @Override public <T> T postProcess(T object) { return object; } }; /*private static KeyPair getKeyPair() { KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource( keyStoreFile), keyStorePassword.toCharArray()); return keyStoreKeyFactory.getKeyPair(keyPairAlias); }*/ @Bean @ConditionalOnMissingBean(TokenStore.class) public TokenStore redisTokenStore() { return new RedisTokenStore(lettuceConnectionFactory); } /*@Primary @ConditionalOnMissingBean(TokenStore.class) private TokenStore jwtTokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); }*/ /*@Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setKeyPair(getKeyPair()); return converter; }*/ /** * 采用内存模型 * * @param clients 客户端详情服务配置器 * @throws Exception 配置客户端详情异常 * @apiNote clients.withClientDetails(clientDetailsService);通过该方法可以注入自定义的客户端详情服务 */ @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient(clientId) .scopes("xx") .secret(EncryptionPrefix.BCRYPT.prefix + passwordEncoder.encode(clientSecret)) .authorizedGrantTypes(GRANT_TYPE_PASSWORD, REFRESH_TOKEN) .authorizedGrantTypes(CLIENT_CREDENTIALS) .accessTokenValiditySeconds((int) TimeUnit.MINUTES.toSeconds(30)); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); /*tokenEnhancerChain.setTokenEnhancers( Collections.singletonList(jwtAccessTokenConverter()));*/ // 1小时 endpoints.tokenStore(redisTokenStore()) .tokenEnhancer(tokenEnhancerChain) .authenticationManager(authenticationManager); // 配置tokenServices参数 DefaultTokenServices tokenServices = new DefaultTokenServices(); //token store tokenServices.setTokenStore(endpoints.getTokenStore()); //支持refresh_token tokenServices.setSupportRefreshToken(true); //ClientDetailsService tokenServices.setClientDetailsService(endpoints.getClientDetailsService()); //token converter tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer()); tokenServices.setAccessTokenValiditySeconds(getAccessTokenValiditySeconds()); endpoints.tokenServices(tokenServices) .exceptionTranslator(exceptionTranslator); } @Override public void configure(AuthorizationServerSecurityConfigurer security) { // 允许表单认证 security.allowFormAuthenticationForClients() .tokenKeyAccess("permitAll()") .checkTokenAccess("isAuthenticated()") .authenticationEntryPoint(authExceptionEntryPoint) .accessDeniedHandler(accessDeniedHandler); } private int getAccessTokenValiditySeconds() { if (validityHour != 0) { return (int) TimeUnit.HOURS.toSeconds(validityHour); } return 0; } @Autowired private void basicAuthenticationFilter(ClientDetailsService clientDetailsService) { try { ClientDetailsUserDetailsService clientDetailsUserDetailsService = new ClientDetailsUserDetailsService(clientDetailsService); clientDetailsUserDetailsService.setPasswordEncoder(passwordEncoder); AuthenticationManagerBuilder managerBuilder = new AuthenticationManagerBuilder(objectPostProcessor); managerBuilder.userDetailsService(clientDetailsUserDetailsService) .addObjectPostProcessor(objectPostProcessor); /* BasicAuthenticationFilter .setAuthenticationManager(managerBuilder.build()); */ } catch (Exception e) { e.printStackTrace(); } } @Override public void afterPropertiesSet() throws Exception { } }
package com.yuan.oauth2demo.conf; import com.yuan.oauth2demo.common.exception.ServiceException; import com.yuan.oauth2demo.common.http.ResponseResult; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.InternalAuthenticationServiceException; import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; import org.springframework.security.oauth2.common.exceptions.UnsupportedGrantTypeException; import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator; import org.springframework.stereotype.Component; @SuppressWarnings("unchecked") @Component public class AuthorizationTokenResponseExceptionTranslator implements WebResponseExceptionTranslator<OAuth2Exception> { private static final String INVALID_REFRESH_TOKEN = "Invalid refresh token"; @Override public ResponseEntity translate(Exception e) { String message = e.getMessage(); if (e instanceof InvalidGrantException) { if (message.contains(INVALID_REFRESH_TOKEN)) { return new ResponseEntity<>(new ResponseResult<>(ServiceException.Problems.EXCEPTION.getCode(), "refresh token不可用"), HttpStatus.UNAUTHORIZED); } return new ResponseEntity<>(new ResponseResult<>(ServiceException.Problems.EXCEPTION.getCode(), "账户信息有误"), HttpStatus.UNAUTHORIZED); } if (e instanceof UnsupportedGrantTypeException) { return new ResponseEntity<>(new ResponseResult<>(ServiceException.Problems.EXCEPTION.getCode(), "不支持授权类型:" + message.substring(message.indexOf(":") + 1)), HttpStatus.UNAUTHORIZED); } if (e instanceof InternalAuthenticationServiceException) { if (String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()).contains(message)) { return new ResponseEntity<>(new ResponseResult<>(ServiceException.Problems.EXCEPTION.getCode(), "当前机构未注册"), HttpStatus.UNAUTHORIZED); } return new ResponseEntity<>(new ResponseResult<>(ServiceException.Problems.EXCEPTION.getCode(), "访问超时,请重试"), HttpStatus.INTERNAL_SERVER_ERROR); } return new ResponseEntity<>(new ResponseResult<>(ServiceException.Problems.EXCEPTION.getCode(), "未知异常"), HttpStatus.UNAUTHORIZED); } }
package com.yuan.oauth2demo.conf; import com.alibaba.fastjson.JSON; import com.yuan.oauth2demo.common.exception.ServiceException; import com.yuan.oauth2demo.common.http.ResponseResult; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component("customAccessDeniedHandler") public class CustomAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) { response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE); try { response.getWriter() .write(JSON.toJSONString( new ResponseResult<String>(ServiceException.Problems.EXCEPTION.getCode(), "访问被拒绝"))); } catch (IOException e) { e.printStackTrace(); } } }
package com.yuan.oauth2demo.conf; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.yuan.oauth2demo.utils.RedisHelper; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.data.redis.RedisProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisPassword; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import javax.annotation.Resource; @Configuration @ConditionalOnProperty(name = "oatu2demo.redis.enable", havingValue = "true") public class RedisClientConfiguration { public static final String REDIS_TEMPLATE_FOR_OBJECT = "redisTemplateForObject"; private static final Logger lg = LoggerFactory.getLogger(RedisClientConfiguration.class); @Resource private RedisProperties redisProperties; @Bean("redisHelper") public RedisHelper redisHelper() { return new RedisHelper(createRedisConnectionFactory()); } @Bean("redisConnectionFactory") @ConditionalOnMissingBean(LettuceConnectionFactory.class) public LettuceConnectionFactory createRedisConnectionFactory() { RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); redisStandaloneConfiguration.setHostName(redisProperties.getHost()); redisStandaloneConfiguration.setPort(redisProperties.getPort()); redisStandaloneConfiguration.setDatabase(redisProperties.getDatabase()); redisStandaloneConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword())); LettuceClientConfiguration.LettuceClientConfigurationBuilder lettuceClientConfigurationBuilder = createBuilder(redisProperties.getLettuce().getPool()); // connection timeout lettuceClientConfigurationBuilder.commandTimeout(redisProperties.getTimeout()); return new LettuceConnectionFactory(redisStandaloneConfiguration, lettuceClientConfigurationBuilder.build()); } private LettuceClientConfiguration.LettuceClientConfigurationBuilder createBuilder(RedisProperties.Pool pool) { if (pool == null) { return LettuceClientConfiguration.builder(); } return LettucePoolingClientConfiguration.builder() .poolConfig(getPoolConfig(pool)); } private <T> GenericObjectPoolConfig<T> getPoolConfig(RedisProperties.Pool properties) { GenericObjectPoolConfig<T> config = new GenericObjectPoolConfig<>(); config.setMaxTotal(properties.getMaxActive()); config.setMaxIdle(properties.getMaxIdle()); config.setMinIdle(properties.getMinIdle()); if (properties.getTimeBetweenEvictionRuns() != null) { config.setTimeBetweenEvictionRunsMillis(properties.getTimeBetweenEvictionRuns().toMillis()); } if (properties.getMaxWait() != null) { config.setMaxWaitMillis(properties.getMaxWait().toMillis()); } return config; } @Bean(REDIS_TEMPLATE_FOR_OBJECT) public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { if (null == redisConnectionFactory) { lg.error("Redis Template Service is not available"); return null; } //设置序列化 Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); // 配置redisTemplate RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); RedisSerializer stringSerializer = new StringRedisSerializer(); // key序列化 redisTemplate.setKeySerializer(stringSerializer); // value序列化 redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); // Hash key序列化 redisTemplate.setHashKeySerializer(stringSerializer); // Hash value序列化 redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } }
package com.yuan.oauth2demo.conf; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; import org.springframework.security.oauth2.provider.token.TokenStore; @Configuration @EnableConfigurationProperties(ResourceServerConfigurationProperties.class) @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true) public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { private final TokenStore tokenStore; private final AuthExceptionEntryPoint exceptionEntryPoint; private final ResourceServerConfigurationProperties configurationProperties; @Autowired public ResourceServerConfiguration(@Qualifier("redisTokenStore") TokenStore tokenStore, AuthExceptionEntryPoint exceptionEntryPoint, ResourceServerConfigurationProperties configurationProperties) { this.tokenStore = tokenStore; this.exceptionEntryPoint = exceptionEntryPoint; this.configurationProperties = configurationProperties; } @Override public void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers(configurationProperties.getPermitted()).permitAll() .antMatchers(configurationProperties.getAuthenticated()).authenticated(); } @Override public void configure(ResourceServerSecurityConfigurer resources) { resources.tokenStore(tokenStore); resources.authenticationEntryPoint(exceptionEntryPoint); } }
package com.yuan.oauth2demo.conf; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "resource.server.configuration") public class ResourceServerConfigurationProperties { /** * 允许不需要认证的url虚拟路径后缀列表 * * @apiNote 默认没有允许的路径 */ private String[] permitted = new String[0]; /** * 需要认证的url虚拟路径后缀列表 * * @apiNote 默认所有路径都需要认证 */ private String[] authenticated = new String[]{"/**"}; public final String[] getPermitted() { return permitted; } public final void setPermitted(String[] permitted) { this.permitted = permitted; } public final String[] getAuthenticated() { return authenticated; } public final void setAuthenticated(String[] authenticated) { this.authenticated = authenticated; } }
package com.yuan.oauth2demo.conf; import com.yuan.oauth2demo.mapper.demo.system.UserMapper; import com.yuan.oauth2demo.service.impl.UserDetailServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.password.PasswordEncoder; @Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter { private final UserMapper userMapper; @Autowired public SecurityConfiguration(UserMapper userMapper) { this.userMapper = userMapper; } @Bean("delegatingPasswordEncoder") PasswordEncoder passwordEncoder() { return PasswordEncoderFactories.createDelegatingPasswordEncoder(); } @Bean("bCryptPasswordEncoder") PasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(HttpSecurity http) throws Exception { http.requestMatchers().anyRequest() .and() .authorizeRequests() .antMatchers("/oauth/**", "/v2/api-docs").permitAll() .antMatchers("/**").permitAll() .antMatchers("/**").authenticated(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailService(userMapper)).passwordEncoder(passwordEncoder()); } @Bean public UserDetailsService userDetailService(UserMapper userMapper) { return new UserDetailServiceImpl(userMapper, bCryptPasswordEncoder()); } }
六、controller 用于测试
package com.yuan.oauth2demo.controller; import com.yuan.oauth2demo.common.http.ResponseResult; import com.yuan.oauth2demo.controller.base.BaseController; import com.yuan.oauth2demo.service.UserService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.security.Principal; @RestController @RequestMapping("/user") public class UserController extends BaseController { private final UserService userService; public UserController(UserService userService) { this.userService = userService; } @GetMapping("/detail") ResponseResult findUserByUsername(Principal principal, @RequestParam String username){ return userService.findUserByUsername(username); } }
七、项目总体目录结构

八、调试
1. 获取token
2.使用token,调controller里的方法

3.换取token

完毕!
2.使用token,调controller里的方法
浙公网安备 33010602011771号