1 package cn.home1.oss.lib.security.swagger;
2
3 import static cn.home1.oss.boot.autoconfigure.AppType.RESOURCE;
4 import static com.google.common.base.Predicates.or;
5 import static com.google.common.collect.Lists.newArrayList;
6 import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
7 import static org.springframework.http.MediaType.TEXT_HTML_VALUE;
8 import static org.springframework.web.bind.annotation.RequestMethod.GET;
9 import static org.springframework.web.bind.annotation.RequestMethod.POST;
10 import static springfox.documentation.builders.PathSelectors.regex;
11
12 import com.google.common.base.Optional;
13 import com.google.common.base.Predicate;
14 import com.google.common.collect.Sets;
15
16 import cn.home1.oss.boot.autoconfigure.AppProperties;
17 import cn.home1.oss.boot.autoconfigure.AppSecurityProperties;
18 import cn.home1.oss.lib.common.crypto.KeyExpression;
19 import cn.home1.oss.lib.errorhandle.api.ResolvedError;
20 import cn.home1.oss.lib.security.api.GenericUser;
21 import cn.home1.oss.lib.security.internal.rest.RestfulLoginPublicKeyFilter;
22 import cn.home1.oss.lib.swagger.ManualRequestHandler;
23 import cn.home1.oss.lib.swagger.SwaggerUtils;
24 import cn.home1.oss.lib.swagger.model.ApiOperationInfo;
25 import cn.home1.oss.lib.swagger.model.ApiRequest;
26
27 import com.fasterxml.classmate.TypeResolver;
28
29 import org.springframework.beans.factory.annotation.Autowired;
30 import org.springframework.core.annotation.Order;
31 import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
32 import org.springframework.security.web.authentication.logout.LogoutFilter;
33 import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
34 import org.springframework.web.bind.annotation.RequestMethod;
35 import org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition;
36 import org.springframework.web.servlet.mvc.condition.HeadersRequestCondition;
37 import org.springframework.web.servlet.mvc.condition.ParamsRequestCondition;
38 import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
39 import org.springframework.web.servlet.mvc.condition.ProducesRequestCondition;
40 import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
41
42 import springfox.documentation.RequestHandler;
43 import springfox.documentation.builders.ResponseMessageBuilder;
44 import springfox.documentation.schema.DefaultGenericTypeNamingStrategy;
45 import springfox.documentation.schema.ModelRef;
46 import springfox.documentation.service.ResponseMessage;
47 import springfox.documentation.spi.DocumentationType;
48 import springfox.documentation.spi.service.DocumentationPlugin;
49 import springfox.documentation.spi.service.contexts.DocumentationContext;
50 import springfox.documentation.spi.service.contexts.DocumentationContextBuilder;
51 import springfox.documentation.swagger.common.SwaggerPluginSupport;
52
53 import java.util.EnumMap;
54 import java.util.List;
55 import java.util.Map;
56
57
58
59
60 @Order(value = SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER)
61 public class SecurityApiDocumentationPlugin implements DocumentationPlugin {
62
63 @Autowired
64 private TypeResolver typeResolver;
65 @Autowired
66 private AppProperties appProperties;
67
68 @Override
69 public DocumentationContext configure(final DocumentationContextBuilder documentationContextBuilder) {
70 documentationContextBuilder
71 .apiInfo(SwaggerUtils.apiInfo("oss-lib-security", "oss-lib-security's security endpoints"))
72 .groupName("security")
73 .pathMapping(Optional.absent())
74 .genericsNaming(new DefaultGenericTypeNamingStrategy())
75 .requestHandlers(this.requestHandlers())
76 .additionalModels(Sets.newHashSet(this.typeResolver.resolve(ResolvedError.class)));
77 return documentationContextBuilder.build();
78 }
79
80 private List<RequestHandler> requestHandlers() {
81 final AppSecurityProperties appSecurityProperties = this.appProperties.getSecurity();
82
83
84
85 final List<RequestHandler> result = newArrayList();
86 if (appSecurityProperties.getEnabled() && this.appProperties.getType() != RESOURCE) {
87 result.add(
88 ManualRequestHandler.requestHandlerBuilder()
89 .consumes(new ConsumesRequestCondition())
90 .declaringClass(DefaultLoginPageGeneratingFilter.class)
91 .groupName(DefaultLoginPageGeneratingFilter.class.getSimpleName())
92 .headers(new HeadersRequestCondition())
93 .parameters(newArrayList())
94 .params(new ParamsRequestCondition())
95 .patternsCondition(new PatternsRequestCondition(appSecurityProperties.getLoginPage()))
96 .produces(new ProducesRequestCondition(TEXT_HTML_VALUE))
97 .returnType(this.typeResolver.resolve(String.class))
98 .supportedMethods(new RequestMethodsRequestCondition(GET))
99 .build()
100 );
101 result.add(
102 ManualRequestHandler.requestHandlerBuilder()
103 .consumes(new ConsumesRequestCondition())
104 .declaringClass(UsernamePasswordAuthenticationFilter.class)
105 .groupName(UsernamePasswordAuthenticationFilter.class.getSimpleName())
106 .headers(new HeadersRequestCondition())
107 .parameters(newArrayList(
108
109 ))
110 .apiOperationInfo(ApiOperationInfo.builder()
111 .notes("用户登录请求处理")
112 .apiRequest(ApiRequest.builder()
113 .parameters(newArrayList())
114 .build()
115 .addParameter("username", "enter your user name", true)
116 .addParameter("password", "enter your password", true)
117 .addParameter("_csrf", "Cross-site request forgery", false)
118 .addParameter("remember-me", "So remember me?", false))
119 .build()
120 ).patternsCondition(new PatternsRequestCondition(appSecurityProperties.getLoginProcessingUrl()))
121 .produces(new ProducesRequestCondition(APPLICATION_JSON_VALUE))
122 .returnType(this.typeResolver.resolve(GenericUser.class))
123 .supportedMethods(new RequestMethodsRequestCondition(POST))
124 .build()
125
126 );
127 result.add(
128 ManualRequestHandler.requestHandlerBuilder()
129 .consumes(new ConsumesRequestCondition())
130 .declaringClass(RestfulLoginPublicKeyFilter.class)
131 .groupName(RestfulLoginPublicKeyFilter.class.getSimpleName())
132 .headers(new HeadersRequestCondition())
133 .parameters(newArrayList())
134 .params(new ParamsRequestCondition())
135 .patternsCondition(new PatternsRequestCondition(appSecurityProperties.getLoginPublicKeyUrl()))
136 .produces(new ProducesRequestCondition(APPLICATION_JSON_VALUE))
137 .returnType(this.typeResolver.resolve(KeyExpression.class))
138 .supportedMethods(new RequestMethodsRequestCondition(GET))
139 .build()
140 );
141 result.add(
142 ManualRequestHandler.requestHandlerBuilder()
143 .consumes(new ConsumesRequestCondition())
144 .declaringClass(LogoutFilter.class)
145 .groupName(LogoutFilter.class.getSimpleName())
146 .headers(new HeadersRequestCondition())
147 .parameters(newArrayList())
148 .params(new ParamsRequestCondition())
149 .patternsCondition(new PatternsRequestCondition(appSecurityProperties.getLogoutUrl()))
150 .produces(new ProducesRequestCondition(APPLICATION_JSON_VALUE))
151 .returnType(this.typeResolver.resolve(Void.class))
152 .supportedMethods(new RequestMethodsRequestCondition(POST))
153 .build()
154 );
155 }
156 return result;
157 }
158
159 @Override
160 public DocumentationType getDocumentationType() {
161 return DocumentationType.SWAGGER_2;
162 }
163
164 @Override
165 public String getGroupName() {
166 return "security";
167 }
168
169 @Override
170 public boolean isEnabled() {
171 return true;
172 }
173
174 @Override
175 public boolean supports(final DocumentationType delimiter) {
176 return DocumentationType.SWAGGER_2.equals(delimiter);
177 }
178
179
180
181
182
183 @Deprecated
184 public Predicate<String> securityPaths() {
185 final String securityBasePath = this.appProperties.getSecurity().getBasePath();
186 return or(regex(securityBasePath + "/.*"), regex("/oauth/.*"));
187 }
188
189 private static List<ResponseMessage> responseMessages() {
190 final List<ResponseMessage> responseMessages = newArrayList();
191 responseMessages.add(new ResponseMessageBuilder()
192 .code(400).message("400diy")
193 .responseModel(new ModelRef("cn.home1.oss.lib.errorhandle.api.ResolvedError"))
194 .build());
195 return responseMessages;
196 }
197
198
199
200
201
202 @Deprecated
203 static Map<RequestMethod, List<ResponseMessage>> additionalResponseMessages() {
204 final Map<RequestMethod, List<ResponseMessage>> additionalResponseMessages = new EnumMap<>(RequestMethod.class);
205 final List<ResponseMessage> responseMessages = responseMessages();
206 for (final RequestMethod requestMethod : RequestMethod.values()) {
207 additionalResponseMessages.put(requestMethod, responseMessages);
208 }
209 return additionalResponseMessages;
210 }
211 }