Session && Cookie
Before, i only knew session is used to store data in the server and cookie is used to store data in the Client. However, it is far from enough at work.
For example:
If the user is logged in, we need to use session
to store user’s information. Meanwhile, the server **automatically **storedssessionid
within cookie
and sends the cookie to the Client. We can see the set_Cookie
attribute in the response header. and the Cookie:JSESSIONID=....
attribute in the request header.
notice:
the programmer is not need to operate the sessionId
in the cookie , the browser will automatically maintain cookie.
Unless the cookie is set by yourself.
Q: if Cookie is disabled in the browsers, what should we do?
method : add cookie in the back end on your own initiative
response.setContentType("text/html;charset=utf-8");
HttpSession session = request.getSession();
session.setAttribute("data", "saveDat");
//save sessionid in cookie
Cookie cookie = new Cookie("JSESSION", session.getId());
//set a valid time
cookie.setMaxAge(60*20);
response.addCookie(cookie);
//when its first time to create session in the client, the server will create a new Session. or get the old sessionId.
String data = (String)request.getSession().getAttribute("data");
Cookie Properties
Properties: name, value, maxAge, secure, path, domain, comment, version HttpOnly.
maxAge
: valid time. unit: second. if the value is negative number, it means the cookie is temporary . When the browser closes, the cookie is invalid. if the value is equal to 0, it means the cookie will be deleted. The default value is -1
secure
: The HTTP Protocol is not only stateless, but also insecure. The data using HTTP Protocol spreads directly on the network without any encryption. when cookie.setSecure(true)
, the browser will spread the data with some secure protocols like SSL
and HTTPS
notice: it does not encrypt cookie content.
The following code will output all cookies
<script>document.write(document.cookie);</script>
so we can set HttpOnly
property so that js
can not read cookie . It can effectively prevent XSS
attack
reference:
set httpOnly
attribute using Filter
distributed session and session theory
Cross-origin resource sharing (CORS)
For security reasons, browsers prohibit AJAX calls to resource residing outside the current origin. Cross-origin resource sharing (CORS) is a W3C specification implemented by most browsers that allows you to specify in a flexible way what kind of cross domain requests are authorized, instead of using some less secured and less powerful hacks like IFRAME or JSONP ——–spring website && CORS
Three key points: the same protocol, the same port , the same domain.
Access-Control-Allow-Origin : return the value the origin request header, “*”, “null”.
notice: string “null”
Access-Control-Allow-Credentials: indicate whether the response to request can be exposed.
Access-Control-Max-Age: valid time
Access-Control-Allow-Methods : POST, GET , OPTIONS etc. notice : use OPTIONS method for preflight request.
……..
about preflight request —chinese description
how to do?
solution 1: rewrite route did not try it…
solution 2: set tomcat did not try it….
solution 3:
angular6: Interceptor
import { Injectable } from '@angular/core';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpErrorResponse } from '@angular/common/http';
import {Observable, throwError} from 'rxjs';
import { catchError, retry } from 'rxjs/operators';
import { ActivatedRoute, Router } from '@angular/router';
import { CookieService } from "ngx-cookie-service";
@Injectable()
export class WithcredentialsInterceptor implements HttpInterceptor {
constructor(
private _cookieService:CookieService,
private router: Router,
){}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
var request;
var temp = req.body;
if(req.url.indexOf('backend/')>-1 && req.url.indexOf('backend/user/login') == -1){
console.log(req.url,'cookie ', this._cookieService.get("SHGJSESSIONID"))
temp.createTime = null;
temp.updateTime = null;
request = req.clone({
withCredentials: true,
setHeaders:{"Content-Type": "application/x-www-form-urlencoded"},
// body:JSON.stringify(req.body)
body:temp
});
}else{
request = req;
}
return next.handle(request).pipe(retry(3)).pipe(catchError((err : any) =>{
if (err instanceof HttpErrorResponse) {
if (err.status === 401) {
this.router.navigate(['/login']);
}
return throwError(err);
}
}
));
}}
notice:withCredential
: true; Content-Type: “application/x-www-form-urlencoded”.
if ContentType
is application/json
, it will not work. reason:important
spring boot:
public class SecurityInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
response.setHeader("Access-Control-Allow-Credentials","true");
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH");
HttpSession session = request.getSession();
if (session.getAttribute(WebSecurityConfig.SESSION_KEY) != null) {
return true;
} else {
Cookie[] cookies = request.getCookies();
if(cookies != null){
for(Cookie cookie : cookies){
if(session.getId().equals(cookie.getValue())){
return true;
}
}
}
response.setStatus(401);
// response.setHeader("Cache-Control", "no-store");
// response.setDateHeader("Expires", 0);
// response.setHeader("WWW-authenticate", "Basic Realm=\"請先登錄系統\"");
return false;
}
// return true;
}
}
//we need to pass sessionId to angular. because if the cookie is disabled.
// save store sessionId within cookie in the angular.
//reference:https://blog.csdn.net/fwk19840301/article/details/80675547
//https://blog.csdn.net/woshiyeguiren/article/details/79194003
notice:
www-authenticate
: i do not suggest setting the attribute. if you set it and your response status is 4xx
, the browsers will pop up a login prompt.
reference:
https://stackoverflow.com/questions/86105/how-can-i-suppress-the-browsers-authentication-dialog
some people write the following:
@Configuration
public class WebSecurityConfig extends WebMvcConfigurerAdapter {
/**
* 登錄session key
*/
public static final String SESSION_KEY = "user";
@Bean
public SecurityInterceptor getSecurityInterceptor() {
return new SecurityInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
InterceptorRegistration addInterceptor = registry.addInterceptor(getSecurityInterceptor());
// 排除配置
addInterceptor.excludePathPatterns("/backend/user/login");
addInterceptor.excludePathPatterns("/backend/user/success");
addInterceptor.excludePathPatterns("/backend/user/loginout");
addInterceptor.excludePathPatterns("/login**");
// 攔截配置
addInterceptor.addPathPatterns("/backend/**");
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH")
.allowCredentials(true).maxAge(3600);
}
}
addCorsMappings
method does not work. Access-Control-Allow-Origin should not be ‘*’; so need a filter or interceptor to set Access-Control-Allow-Origin
reference:
http://www.ruanyifeng.com/blog/2016/04/cors.html
https://blog.csdn.net/qq779446849/article/details/53102925
https://blog.csdn.net/saytime/article/details/74937204
BUG
Because Content-Type is application/x-www-form-urlencoded,
For application/x-www-form-urlencoded, the body of the HTTP message sent to the server is essentially one giant query string – name/value pairs are separated by the ampersand (&), and names are separated from values by the equals symbol (=). An example of this would be:
MyVariableOne=ValueOne&MyVariableTwo=ValueTwo
When you send the value encrypted by base64 with jsonString format, you may meet a parse bug.
example:
var data={
password: btoa((this.validateForm.controls['password'].value)),
oldPassword: btoa((this.validateForm.controls['oldPassword'].value)),
userName: this.username
}
this.indexService.changePassword(data).subscribe(
(response :any) =>{
this.isOkLoading = false;
this.isVisible = false;
if(response.status == environment.SUCCESS_STATUS){
this.router.navigate(["login"]);
}else{
this.validateForm.reset();
this._message.error("修改失敗");
}
}
)
//angular6 base64 ---btoa
when the password is 1234567 , the value encrypted by base64 will end with the double equals symbols ( = =),
and the browsers will parse == into :=
so i have to do this:
var data ="password="+btoa((this.validateForm.controls['password'].value))+"&"+"oldPassword="+btoa((this.validateForm.controls['oldPassword'].value))
+"&" +"userName="+this.username;
and the browsers will parse the value into key/value pairs.
like :
FormData:
password:....
oldPassWord: .....
userName:.....