程序目录结构
wx.aop
package com.example.emos.wx.aop;
import com.example.emos.wx.common.util.R;
import com.example.emos.wx.config.shiro.ThreadLocalToken;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class TokenAspect {
@Autowired
private ThreadLocalToken threadLocalToken;
@Pointcut("execution(public * com.example.emos.wx.controller.*.*(..))")
public void aspect(){
}
@Around("aspect()")
public Object around(ProceedingJoinPoint point) throws Throwable{
R r=(R)point.proceed();
String token=threadLocalToken.getToken();
if(token!=null){
r.put("token",token);
threadLocalToken.clear();
}
return r;
}
}
wx.common.util
wx.configpackage com.example.emos.wx.common.util; import org.apache.http.HttpStatus; import java.util.HashMap; import java.util.Map; public class R extends HashMap{ public R() { put("code", HttpStatus.SC_OK); put("msg", "success"); } public R put(String key, Object value) { super.put(key, value); return this; } public static R ok() { return new R(); } public static R ok(String msg) { R r = new R(); r.put("msg", msg); return r; } public static R ok(Map map) { R r = new R(); r.putAll(map); return r; } public static R error(int code, String msg) { R r = new R(); r.put("code", code); r.put("msg", msg); return r; } public static R error(String msg){ return error(HttpStatus.SC_INTERNAL_SERVER_ERROR,msg); } public static R error(){ return error(HttpStatus.SC_INTERNAL_SERVER_ERROR,"未知异常,请联系管理员"); } }
package com.example.emos.wx.config.shiro;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
@Slf4j
public class JwtUtil {
@Value("${emos.jwt.secret}")
private String secret;
@Value("${emos.jwt.expire}")
private int expire;
public String createToken(int userId){
Date date = DateUtil.offset(new Date(), DateField.DAY_OF_YEAR,5);
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTCreator.Builder builder = JWT.create();
String token = builder.withClaim("userId",userId).withExpiresAt(date).sign(algorithm);
return token;
}
public int getUserId(String token){
DecodedJWT jwt = JWT.decode(token);
int userId = jwt.getClaim("userId").asInt();
return userId;
}
public void verifierToken(String token){
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm).build();
verifier.verify(token);
}
}
package com.example.emos.wx.config.shiro;
import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import org.apache.http.HttpStatus;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
@Component
@Scope("prototype")
public class OAuth2Filter extends AuthenticatingFilter {
@Autowired
private ThreadLocalToken threadLocalToken;
@Value("${emos.jwt.cache-expire}")
private int cacheExpire;
@Autowired
private JwtUtil jwtUtil;
@Autowired
private RedisTemplate redisTemplate;
@Override
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest req = (HttpServletRequest) request;
String token = getRequestToken(req);
if(StrUtil.isBlank(token)){
return null;
}
return new OAuth2Token(token);
}
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
HttpServletRequest req = (HttpServletRequest) request;
if(req.getMethod().equals(RequestMethod.OPTIONS.name())){
return true;
}
return false;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
resp.setContentType("text/html");
resp.setCharacterEncoding("UTF-8");
resp.setHeader("Access-Control-Allow-Credentials", "true");
resp.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));
threadLocalToken.clear();
String token = getRequestToken(req);
if(StrUtil.isBlank(token)){
resp.setStatus(HttpStatus.SC_UNAUTHORIZED);
resp.getWriter().print("无效的令牌");
return false;
}
try{
jwtUtil.verifierToken(token);
}catch (TokenExpiredException e){
if(redisTemplate.hasKey(token)){
redisTemplate.delete(token);
int userId = jwtUtil.getUserId(token);
token = jwtUtil.createToken(userId);
redisTemplate.opsForValue().set(token,userId+"",cacheExpire, TimeUnit.DAYS);
threadLocalToken.setToken(token);
} else{
resp.setStatus(HttpStatus.SC_UNAUTHORIZED);
resp.getWriter().print("令牌已过期");
return false;
}
}catch (Exception e){
resp.setStatus(HttpStatus.SC_UNAUTHORIZED);
resp.getWriter().print("无效的令牌");
return false;
}
boolean bool = executeLogin(request,response);
return bool;
}
@Override
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
resp.setContentType("text/html");
resp.setCharacterEncoding("UTF-8");
resp.setHeader("Access-Control-Allow-Credentials", "true");
resp.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));
resp.setStatus(HttpStatus.SC_UNAUTHORIZED);
try{
resp.getWriter().print(e.getMessage());
}catch (Exception exception){
}
return false;
}
@Override
public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
resp.setContentType("text/html");
resp.setCharacterEncoding("UTF-8");
resp.setHeader("Access-Control-Allow-Credentials", "true");
resp.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));
super.doFilterInternal(request, response, chain);
}
private String getRequestToken(HttpServletRequest request){
String token = request.getHeader("token");
if(StrUtil.isBlank(token)){
token = request.getParameter("token");
}
return token;
}
}
package com.example.emos.wx.config.shiro;
import com.example.emos.wx.db.pojo.TbUser;
import com.example.emos.wx.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Set;
@Component
public class OAuth2Realm extends AuthorizingRealm {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private UserService userService;
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof OAuth2Token;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection collection) {
TbUser user = (TbUser) collection.getPrimaryPrincipal();
int userId = user.getId();
Set permsSet = userService.searchUserPermissions(userId);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setStringPermissions(permsSet);
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String accessToken = (String)token.getPrincipal();
int userId = jwtUtil.getUserId(accessToken);
TbUser user = userService.searchById(userId);
if(user == null){
throw new LockedAccountException("账号已被锁定,请联系管理员");
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,accessToken,getName());
return info;
}
}
package com.example.emos.wx.config.shiro;
import org.apache.shiro.authc.AuthenticationToken;
public class OAuth2Token implements AuthenticationToken {
private String token;
public OAuth2Token(String token) {
this.token = token;
}
@Override
public Object getPrincipal() {
return token;
}
@Override
public Object getCredentials() {
return token;
}
}
package com.example.emos.wx.config.shiro;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean("securityManager")
public SecurityManager securityManager(OAuth2Realm realm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
securityManager.setRememberMeManager(null);
return securityManager;
}
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager,OAuth2Filter filter){
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
Map map = new HashMap<>();
map.put("oauth2",filter);
shiroFilter.setFilters(map);
Map filterMap = new LinkedHashMap<>();
filterMap.put("/webjars/**", "anon");
filterMap.put("/druid/**", "anon");
filterMap.put("/app/**", "anon");
filterMap.put("/sys/login", "anon");
filterMap.put("/swagger/**", "anon");
filterMap.put("/v2/api-docs", "anon");
filterMap.put("/swagger-ui.html", "anon");
filterMap.put("/swagger-resources/**", "anon");
filterMap.put("/captcha.jpg", "anon");
filterMap.put("/user/register", "anon");
filterMap.put("/user/login", "anon");
filterMap.put("/test/**", "anon");
filterMap.put("/meeting/recieveNotify", "anon");
filterMap.put("/**", "oauth2");
shiroFilter.setFilterChainDefinitionMap(filterMap);
return shiroFilter;
}
@Bean("lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
return new LifecycleBeanPostProcessor();
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
package com.example.emos.wx.config.shiro;
import org.springframework.stereotype.Component;
@Component
public class ThreadLocalToken {
private ThreadLocal local = new ThreadLocal<>();
public void setToken(String token){
local.set(token);
}
public String getToken(){
return local.get();
}
public void clear(){
local.remove();
}
}
package com.example.emos.wx.config.xss;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@WebFilter(urlPatterns = "/*")
public class XssFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request= (HttpServletRequest) servletRequest;
XssHttpServletRequestWrapper wrapper=new XssHttpServletRequestWrapper(request);
filterChain.doFilter(wrapper,servletResponse);
}
@Override
public void destroy() {
}
}
package com.example.emos.wx.config.xss;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HtmlUtil;
import cn.hutool.json.JSONUtil;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;
import java.util.LinkedHashMap;
import java.util.Map;
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
if(!StrUtil.hasEmpty(value)){
value = HtmlUtil.filter(value);
}
return value;
}
@Override
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(name);
if(values != null){
for (int i = 0; i < values.length; i++){
String value = values[i];
if(!StrUtil.hasEmpty(value)){
value = HtmlUtil.filter(value);
}
values[i] = value;
}
}
return values;
}
@Override
public Map getParameterMap() {
Map parameters = super.getParameterMap();
LinkedHashMap map = new LinkedHashMap();
if(parameters != null){
for (String key:parameters.keySet()){
String[] values = parameters.get(key);
for (int i = 0; i < values.length; i++) {
String value = values[i];
if (!StrUtil.hasEmpty(value)) {
value = HtmlUtil.filter(value);
}
values[i] = value;
}
map.put(key,values);
}
}
return map;
}
@Override
public String getHeader(String name) {
String value = super.getHeader(name);
if (!StrUtil.hasEmpty(value)) {
value = HtmlUtil.filter(value);
}
return value;
}
@Override
public ServletInputStream getInputStream() throws IOException {
InputStream in = super.getInputStream();
InputStreamReader reader = new InputStreamReader(in, Charset.forName("UTF-8"));
BufferedReader buffer = new BufferedReader(reader);
StringBuffer body = new StringBuffer();
String line = buffer.readLine();
while(line != null){
body.append(line);
line = buffer.readLine();
}
buffer.close();
reader.close();
in.close();
Map map = JSONUtil.parseObj(body.toString());
Map result = new LinkedHashMap<>();
for(String key:map.keySet()){
Object val = map.get(key);
if(val instanceof String){
if(!StrUtil.hasEmpty(val.toString())){
result.put(key,HtmlUtil.filter(val.toString()));
}
} else {
result.put(key,val);
}
}
String json = JSONUtil.toJsonStr(result);
ByteArrayInputStream bain = new ByteArrayInputStream(json.getBytes());
return new ServletInputStream() {
@Override
public int read() throws IOException {
return bain.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
}
package com.example.emos.wx.config;
import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.ApiSelectorBuilder;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
Docket docket = new Docket(DocumentationType.SWAGGER_2);
ApiInfoBuilder builder = new ApiInfoBuilder();
builder.title("EMOS在线办公系统");
ApiInfo info = builder.build();
docket.apiInfo(info);
ApiSelectorBuilder selectorBuilder = docket.select();
selectorBuilder.paths(PathSelectors.any());
selectorBuilder.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class));
docket = selectorBuilder.build();
ApiKey apiKey = new ApiKey("token", "token", "header");
List apiKeyList = new ArrayList<>();
apiKeyList.add(apiKey);
docket.securitySchemes(apiKeyList);
AuthorizationScope scope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] scopes = {scope};
SecurityReference reference = new SecurityReference("token", scopes);
List refList = new ArrayList();
refList.add(reference);
SecurityContext context = SecurityContext.builder().securityReferences(refList).build();
List cxtList = new ArrayList();
cxtList.add(context);
docket.securityContexts(cxtList);
return docket;
}
}
wx.controller
package com.example.emos.wx.controller.form; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Pattern; @ApiModel @Data public class TestSayHelloForm { // @NotBlank // @Pattern(regexp = "^[\u4e00-\u9fa5]{2,15}$",message = "不符合正则表达式") @ApiModelProperty("姓名") private String name; }
package com.example.emos.wx.controller;
import com.example.emos.wx.common.util.R;
import com.example.emos.wx.controller.form.TestSayHelloForm;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@RestController
@RequestMapping("/test")
@Api("测试Web接口")
public class TestController {
@PostMapping("/sayHello")
@ApiOperation("最简单的测试方法")
public R sayHello(@Valid @RequestBody TestSayHelloForm form) {
return R.ok().put("message", "Hello," + form.getName());
}
}
wx.exception
package com.example.emos.wx.exception;
import lombok.Data;
@Data
public class EmosException extends RuntimeException{
private String msg;
private int code = 500;
public EmosException(String msg) {
super(msg);
this.msg = msg;
}
public EmosException(String msg, Throwable e) {
super(msg, e);
this.msg = msg;
}
public EmosException(String msg, int code) {
super(msg);
this.msg = msg;
this.code = code;
}
public EmosException(String msg, int code, Throwable e) {
super(msg);
this.msg = msg;
this.code = code;
}
}
启动类
package com.example.emos.wx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@SpringBootApplication
@ServletComponentScan
public class EmosWxApiApplication {
public static void main(String[] args) {
SpringApplication.run(EmosWxApiApplication.class, args);
}
}
配置文件
遇到的问题和bug 1.集成swagger后无报错,但是无法运行
server: tomcat: uri-encoding: UTF-8 threads: max: 200 min-spare: 30 connection-timeout: 5000ms port: 8080 servlet: context-path: /emos-wx-api spring: datasource: type: com.alibaba.druid.pool.DruidDataSource druid: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/emos?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai username: root password: 111111 initial-size: 8 max-active: 16 min-idle: 8 max-wait: 60000 test-while-idle: true test-on-borrow: false test-on-return: false redis: database: 0 host: localhost port: 6379 password: jedis: pool: max-active: 1000 max-wait: -1ms max-idle: 16 min-idle: 8 data: mongodb: host: localhost port: 27017 database: emos authentication-database: admin username: admin password: abc123456 mybatis: mapper-locations: classpath*:mapper/*.xml type-aliases-package: com.example.emos.wx.db.pojo configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl map-underscore-to-camel-case: true logging: level: root: info com.example.emos.wx.db.dao : warn pattern: console: "%d{HH:mm:ss} %-5level %msg%n" emos: jwt: secret: abc123456 expire: 5 cache-expire: 10
解决思路:在pom.xml中使用低版本的spring-boot-starter配合2.9.2版本的swagger,由原来idea创建后默认的2.5.0改为2.3.4
swagger版本
2.代码出现报错“'securitySchemes(java.util.List
解决思路: 将swagger的版本从3.0.0改为2.9.2
明后两天学习计划
学习第三章和第四章内容
学习进度和反思- 目前学到第三章第9节
- 在学习新内容的时候,还是按照老师的源码构建项目比较稳妥,不然可能会出现意料之外的错误,比如同样的代码,在不同版本的依赖之下,会出现不同的现象,在原来的版本下是正常运行的,在新版本就有可能出现报错。我今天遇到的就是,spring-boot-starter版本号过高导致集成swagger2.9.2后无法运行。



