Token Verification on APP service side
The interceptor uses the @Authorization
annotation method to intercept the request, and the token information is removed from the HTTP header to verify that it is legitimate. Illegal direct return 401 error, the legal token corresponding to the user key in the request to continue execution. Specific implementation code:
1234567891011121314151617181920212223242526272829303132 |
public boolean prehandle (httpservletrequest request,httpservletresponse response, Object handler) throws Exception {///AS The result is not mapped to the method directly through if (! ( Handler instanceof Handlermethod) {return true;} Handlermethod Handlermethod = (handlermethod) handler; method = Handlermethod.getmethod ();//Get tokenstring token = Request.getheader (httpheadername) from the header; if (toke n! = null && token.startswith (httpheaderprefix) && token.length () > 0) {token = token.substring (http Headerprefix.length ());//verify Tokenstring key = Manager.getkey (token), if (key = null) {//If token verification succeeds, the user ID corresponding to token There is a request to facilitate the subsequent injection of Request.setattribute (Request_current_key, KEY); return true;}} If the validation token fails and the method notes Authorization, a 401 error is returned if (method.getannotation (authorization.class) = null) {Response.setstat US (httpservletresponse.sc_unauthorized); Response.setcharacterencoding ("GBK"); Response.getwriter (). Write ( Unauthorizederrormessage); Response.getwriter (). Close (); return false;} In order to prevent the use of someWrite KEY directly in Request_current_key, set it to Nullrequest.setattribute (Request_current_key, null); return true;} |
After the interceptor, the modified @CurrentUser
parameters are injected using the parser. Remove the user key from the request before it is saved, and get the corresponding user object and inject it into the parameter. Specific implementation code:
123456789101112131415161718192021222324252627282930313233 |
@Overridepublic boolean supportsparameter (methodparameter parameter) {Class clazz;try {clazz = Class.forName ( Usermodelclass);} catch (ClassNotFoundException e) {return false;} If the parameter type is User and a CurrentUser annotation is supported, if (Parameter.getparametertype (). IsAssignableFrom (Clazz) && Parameter.hasparameterannotation (Currentuser.class)) {return true;} return false;} @Overridepublic Object resolveargument (methodparameter parameter, Modelandviewcontainer Mavcontainer, Nativewebrequest webRequest, Webdatabinderfactory binderfactory) throws Exception {//Remove authentication to the logged-in user Idobject object = Webr Equest.getattribute (Authorizationinterceptor.request_current_key, requestattributes.scope_request); if (Object! = NULL) {String key = String.valueof (object);//Query from database and return object Usermodel = Usermodelrepository.getcurrentuser (key); if ( Usermodel! = null) {return usermodel;} There is a key but not the user, throw exception throw new Missingservletrequestpartexception (Authorizationinterceptor.request_current_key);} Return the NU directly without keyLlreturn null;} |
Detailed analysis: RESTful login Design (Token authentication based on Spring and Redis)
See Source: Scienjus/spring-restful-authorization
Packaged Tool class: Scienjus/spring-authorization-manager
Using aliases to accept parameters for an object
It is common for the parameter names in the request to be different from the parameter names defined in the code, and in this case Spring provides several native methods:
For @RequestParam
example, you can directly specify the value as an alias (and @RequestHeader
the same), such as:
123 |
Public String Home (@RequestParam ("user_id") long userId) {return "Hello" + userId;} |
For @RequestBody
, because it makes use of Jackson to convert Json to an object, you can use @JsonProperty
the value to specify an alias, for example:
12345678 |
Public String Home (@RequestBody user user) {return "Hello" + User.getuserid ();} Class User {@JsonProperty ("user_id") Private long UserId;} |
However, when you use an object's properties to accept parameters, you cannot specify the alias directly from the method above, for example:
123 |
Public String Home (user user) {return "Hello" + User.getuserid ();} |
This is the time to use DataBinder to manually bind properties and aliases, and the article I found on the Stack Overflow is a good way to do this, not to repeat the wheel.
Turn off default by request suffix name to Judge Content-type
The development habit of the previously inherited project is to use the. html as the suffix of the request, which is not a problem on Struts2 (because it Struts2 a few ways to handle Json). But when I take over the change to Spring MVC, I'm @ResponseBody
not going to find a converter error when I use the returned object.
This is because Spring MVC defaults to the content-type of the request with the suffix named. html text/html
, and the @ResponseBody
returned Content-type is application/json
that there is no one converter that supports such conversions. So it is necessary to manually turn off the setting of Content-type by suffix and set the default Content-type to application/json
:
123456789 |
@Configurationpublic class Webmvcconfig extends Webmvcconfigureradapter {@Overridepublic void Configurecontentnegotiation (Contentnegotiationconfigurer configurer) {configurer.favorpathextension (false). Defaultcontenttype (Mediatype.application_json);}} |
Change the default Json serialization scheme
Projects sometimes have their own unique Json serialization scheme, such as the more common use 0
/ 1
substitution false
/ true
, or by ""
substitution null
, as the @ResponseBody
default is to use MappingJackson2HttpMessageConverter
, only to implement ObjectMapper
their own Pass in this converter:
1234567891011121314151617181920 |
public class Customobjectmapper extends Objectmapper {public customobjectmapper () {super (); This.getserializerprovider (). Setnullvalueserializer (New jsonserializer<object> () {@Overridepublic void serialize (Object value, Jsongenerator Jgen, Serializerprovider provider) throws IOException {jgen.writestring ("");}}); Simplemodule module = new Simplemodule (); Module.addserializer (Boolean.class, New jsonserializer<boolean> () {@ overridepublic void Serialize (Boolean value, Jsongenerator Jgen, Serializerprovider provider) throws IOException {JGEN.W Ritenumber (value 1:0);}); This.registermodule (module);}} |
Json in an automatic encryption/decryption request
Type conversion issues
Related to @RequestBody
and @ResponseBody
are generally in the mappingjackson2httpmessageconverter
resolved, want to automatically encrypt/decrypt only need to inherit this class and override readinternal
/ writeinternal
method:
12345678910111213141516171819 |
@Overrideprotected Object readinternal (class<?> clazz, Httpinputmessage inputmessage) throws IOException, httpmessagenotreadableexception {//decrypt string json = Aesutil.decrypt (Inputmessage.getbody ()); Javatype Javatype = Getjavatype (clazz, NULL);//Convert return This.objectMapper.readValue (JSON, javatype);} @Overrideprotected void Writeinternal (Object object, Httpoutputmessage outputmessage) throws IOException, httpmessagenotwritableexception {//using Jackson's objectmapper to convert Java objects to Json stringobjectmapper mapper = new Objectmapp ER (); String json = Mapper.writevalueasstring (object);//Encrypt string result = Aesutil.encrypt (JSON);//Output Outputmessage.getbody ( ). Write (Result.getbytes ());} |
Annotation-based sensitive word filtering function
The project needs to filter the content posted by the user and replace the sensitive words with the *
special characters. Most WEB projects handle this requirement by selecting a filter ( Filter
), wrapping a layer in the filter, Request
Wrapper
and overriding methods such as getParameter
:
123456789101112131415161718192021222324252627282930313233343536373839 |
public class Safetextrequestwrapper extends Httpservletrequestwrapper {public Safetextrequestwrapper ( HttpServletRequest req) {super (req);} @Overridepublic map<string, String []> getparametermap () {map<string, string []> parammap = Super.getp Arametermap (); for (String [] values:paramMap.values ()) {for (int i = 0; i < values.length; i++) {values [i] = Sensi Tiveutil.filter (values [i]);}} return parammap;} @Overridepublic string GetParameter (string name) {return Sensitiveutil.filter (Super.getparameter (name));}} public class Safetextfilter implements Filter {@Overridepublic void init (Filterconfig filterconfig) throws Servlet Exception { } @Overridepublic void DoFilter (ServletRequest request, servletresponse response, Filterchain Chain) throws IOException, servletexception {safetextrequestwrapper safetextrequestwrapper = new Safetextrequestwrapper ((httpservletrequest) request); Chain.dofilter (safetextrequestwrapper, response);} @OverriDepublic void Destroy () { }} |
However, there are some obvious problems, such as the inability to control what information is filtered. If the user registers the mailbox or the password also carries the kind fuck
of sensitive words, that is the accidental injury.
So instead of using spring MVC's Formatter to expand, just @RequestParam
use annotations on the parameters @SensitiveFormat
, and Spring MVC automatically filters the sensitive words as it injects the attribute. Both convenient and not accidental, the implementation method is as follows:
Statement @SensitiveFormat
Annotations:
12345 |
@Target ({Elementtype.field, elementtype.parameter}) @Retention (retentionpolicy.runtime) @Documentedpublic @ Interface Sensitiveformat {} |
Create the SensitiveFormatter
class. Implement an Formatter
interface that overrides the method parse
that transforms the received content into an object, filtering the received content in the method:
1234567891011 |
public class Sensitiveformatter implements formatter<string> {@Overridepublic string parse (string text, Locale Loc Ale) throws ParseException {return sensitiveutil.filter (text);} @Overridepublic string Print (String object, Locale Local E) {return object;}} |
Create the SensitiveFormatAnnotationFormatterFactory
class, implement the AnnotationFormatterFactory
interface, @SensitiveFormat
and bind it SensitiveFormatter
:
12345678910111213141516171819 |
public class Sensitiveformatannotationformatterfactory implements Annotationformatterfactory<sensitiveformat > {@Overridepublic set<class<?>> getfieldtypes () {set<class<?>> fieldtypes = new HashSet <> (); Fieldtypes.add (String.class); return fieldtypes;} @Overridepublic printer<?> getprinter (sensitiveformat annotation, class<?> fieldtype) {return new Sensitiveformatter ();} @Overridepublic parser<?> getparser (sensitiveformat annotation, class<?> fieldtype) {return new Sensitiveformatter ();}} |
The final SensitiveFormatAnnotationFormatterFactory
registration will be in Spring MVC:
123456789 |
@Configurationpublic class Webmvcconfig extends Webmvcconfigureradapter {@Overridepublic void Addformatters ( Formatterregistry registry) {Registry.addformatterforfieldannotation (new Sensitiveformatannotationformatterfactory ()); Super.addformatters (registry);}} |
Log the returned content of the request
Here is a more general approach, based on the filter implementation, so projects in non-Spring MVC can also be used.
First Import commons-io
:
12345 |
<dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId>< Version>2.4</version></dependency> |
Need to use this library TeeOutputStream
, this class can be a content output to two branches of the output stream, which is encapsulated as ServletOutputStream
:
1234567891011121314151617181920212223242526272829303132333435 |
public class Teeservletoutputstream extends Servletoutputstream {private final teeoutputstream teeoutputstream; Eeservletoutputstream (OutputStream one, outputstream) {this.teeoutputstream = new Teeoutputstream (one, both);} @Over Ridepublic Boolean IsReady () {return false;} @Overridepublic void Setwritelistener (Writelistener listener) {} @Override public void Write (int b) throws IOException {this.teeOutputStream.write (b);} @Overridepublic void Flush () throws Ioexce ption {Super.flush (); This.teeOutputStream.flush (); @Overridepublic void Close () throws IOException {super.close (); th Is.teeOutputStream.close ();}} |
Then create a filter that overrides the original response
getOutputStream
method:
123456789101112131415161718192021222324252627282930313233 |
public class Loggingfilter implements Filter { private static final Logger Logger = Loggerfactory.getlogger (LOGGINGF Ilter.class); @Overridepublic void init (Filterconfig filterconfig) throws Servletexception { } public void DoFilter (ServletRequest request, Final servletresponse response, Filterchain chain) throws IOException, Servletexce ption {final Bytearrayoutputstream bytearrayoutputstream = new Bytearrayoutputstream (); Httpservletresponsewrapper responsewrapper = new Httpservletresponsewrapper ((httpservletresponse) response) { Private Teeservletoutputstream teeservletoutputstream; @Overridepublic servletoutputstream getoutputstream () Throws IOException {return new Teeservletoutputstream (Super.getoutputstream (), Bytearrayoutputstream);}}; Chain.dofilter (Request, responsewrapper); String Responselog = bytearrayoutputstream.tostring (); if (logger.isinfoenabled () &&! Stringutil.isempty (Responselog)) {logger.info (Responselog);}} @Overridepublic void Destroy () { }} |
super.getOutputStream ()
and ByteArrayOutputStream
respectively as two sub-branches, the former will return the content to the client, the latter use the toString
method to obtain the output content.
Other thought to add ...
The original is transferred from
Scienjus ' s Blog
Some tips for using SPRING-MVC (turn)