Last Updated on: February 8, 2021 pm
                
              
            
            
              Spring Security - Overview Model 
Spring Security defines a framework for Security. 
Implmented using Servlet filters in the background. 
Two methods of securing a Web app: Declarative and Programmatic. 
 
Big Pic 
Flow Chart in Action 
Security Concepts 
Authentication - Check user id or password with credentials stored in App/db. 
Authorization - Check to see if user has an authorized role. 
 
Declarative Security Defines application’s security constraints in configuration. 
All Java config  
Or Spring XML Config 
 
Programmatic Security 
Login Methods 
HTTP 
Default Login Form 
Custom Login Form 
 
Spring MVC - Java Config Add Maven Dependencies Import existing maven project into IntelliJ.
Since we are not using web.xml, customize Maven build and must add Maven WAR plugin.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <build > <finalName > spring-security-demo</finalName > <pluginManagement > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-war-plugin</artifactId > <version > 3.3.1</version > </plugin > </plugins > </pluginManagement > </build > 
Create Spring @Configuration Create a DemoAppConfig.java file in the source directory.
1 2 3 4 5 6 7 8 9 10 11 12 13 @Configuration @EnableWebMvc @ComponentScan(basePackages = "com.luv2code.springsecurity.demo") public  class  DemoAppConfig  @Bean public  ViewResolver viewResolver () new  InternalResourceViewResolver();"/WEB-INF/view/" );".jsp" );return  viewResolver;
Create Spring Dispatcher Servlet Initializer Spring MVC provides support for web app initialization.
Your code is used to initialize the servlet container.
AbstractAnnotationConfigDispatcherServletInitializer 
Extend the abstract base class 
Override some required methods 
Specify  servlet mapping and location of your app config 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public  class  MySpringMvcDispatcherServletInitializer  extends  AbstractAnnotationConfigDispatcherServletInitializer  @Override protected  Class<?>[] getRootConfigClasses() {return  new  Class[0 ];@Override protected  Class<?>[] getServletConfigClasses() { return  new  Class[]{ DemoAppConfig.class }; @Override protected  String[] getServletMappings() { return  new  String[] { "/"  }; 
Develop Spring Controller 1 2 3 4 5 6 7 @Controller public  class  DemoController  @GetMapping("/") public  String showHome () return  "home" ;
Develop JSP View Page Create a test.jsp file and run the tomcat server to test the demo.
Create Spring Security Initializer AbstractSecurityWebApplicationInitializer A special class to register the Spring Security Filters. 
TODO List 
1 2 3 public  class  SecurityWebApplicationInitializer         extends  AbstractSecurityWebApplicationInitializer   {
Create Spring Security Configuration 1 2 3 4 5 6 7 8 9 @Configuration @EnableWebSecurity public  class  DemoSecurityConfig         extends  WebSecurityConfigurerAdapter   {@Override protected  void  configure (AuthenticationManagerBuilder auth)  throws  Exception 
Add Users Passwords and Roles Add users in DemoSecurityConfig.java
1 2 3 4 5 6 "John" ).password("test123" ).roles("EMPLOYEE" ))"Mary" ).password("test123" ).roles("MANAGER" ))"Susan" ).password("test123" ).roles("ADMIN" ));
Modify Spring Security Configuration To reference custom login form.
1 2 3 4 5 6 7 8 9 10 @Override protected  void  configure (HttpSecurity http)  throws  Exception "/showMyLoginPage" )"/authenticateTheUser" )
Override the configure method.
Develop a Controller To show the custom login form.
1 2 3 4 5 6 7 @Controller public  class  LoginController  @GetMapping("/showMyLoginPage") public  String showMyLoginPage () return  "plain-login" ;
Why use Context Path? 
Allows us to dynamically reference context path of application. 
Helps to keep links relative to application context path.LL 
If you change context path of app, then links will still work. 
Much better than hard-coding the context path. 
 
1 2 3 4 5 6 7 8 9 <body>"${pageContext.request.contextPath}/authenticateTheUser"  method="post" >"submit"  value="Login" />
Add Error Message 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <%@ taglib prefix="c"  uri="http://java.sun.com/jsp/jstl/core"  %>"${pageContext.request.contextPath}/authenticateTheUser"  method="post" >if  test="${param.error != null}" >"submit"  value="Login" />
Note:  Change http://java.sun.com/jstl/core to http://java.sun.com/jsp/jstl/core, unless you will get Error:  attribute test does not accept any expressions.
Add Logout Support Add Logout to Configuration 1 2 3 4 5 6 7 8 9 10 11 12 @Override protected  void  configure (HttpSecurity http)  throws  Exception "/showMyLoginPage" )"/authenticateTheUser" )
Note:  This will add logout support for default URL /logout
The button needs to send data to the default URL /logout
Logout URL will be handled by Spring Security Fileters - No coding required.   
1 2 3 <form:form action="${pageContext.request.contextPath}/logout"  method="post" >"submit"  value="Logout" >
1 2 3 4 5 <c:if  test="${param.logout != null}" >class "alert alert-success col-xs-offset-1 col-xs-10" >
Cross Site Request Forgery(CSRF) What is CSRF? 
CSRF(Cross Site Request Forgery)即跨站请求伪造。就是利用后台有规律的接口,例如 localhost/deleteAriticle.php?id=3&username=xiaoxiao  ,攻击者在被攻击的网站页面嵌入这样的代码,当用户xiaoxiao访问该网站的时候,会发起这条请求。服务器会删除id为3的数据。
 
Spring Security CSRF Protection Spring Security uses the Synchronizer Token Pattern .
Each request includes a session cookie and randomly generated token.  
 
For request processing, Spring Security verifies token before processing.
All of this is handled by Spring Security Filters automatically.
When to use CSRF Protection? Use CSRF protection for any normal browser web requests.
How to use CSRF Protection? For form submissions use POST  instead of GET .
Just use Spring MVC Form Tags. 
<form:form> will automatically adds CSRF token.
Manually Add CSRF Token 1 2 3 4 5 <form  action =""  method ="" > <input  type ="hidden"           name ="${_csrf.parameterName}"           value ="${_csrf.token}" /> </form > 
If you use plain <form> tag, then you need to manually add CSRF token authentication in the form submission.
Break It Use plain <form> tag rather than Spring MVC <form:form> tag.
1 <form action="${pageContext.request.contextPath}/authenticateTheUser"  method="POST"  class "form-horizontal" >
Then, we will get an ugly 403 - Forbidden error message. 
The server understood the request but refused to authorize it.
 
Fix It Add token manually like mentioned above.
Display Username and Roles Add new Dependency in pom.xml 1 2 3 4 5 <dependency > <groupId > org.springframework.security</groupId > <artifactId > spring-security-taglibs</artifactId > <version > ${springsecurity.version}</version > </dependency > 
Update JSP Page 1 2 3 4 5 <%@ taglib prefix="security"  uri="http://www.springframework.org/security/tags"  %>"principal.username" />"principal.authorities" />
Restrict Access based on Role Create Controller and Views 1 2 3 4 5 6 7 8 9 @GetMapping("/leader") public  String showLeaders () return  "leader" ;@GetMapping("/system") public  String showSystem () return  "system" ;
Update Roles 1 2 3 4 auth.inMemoryAuthentication()"John" ).password("test123" ).roles("EMPLOYEE" ))"Mary" ).password("test123" ).roles("EMPLOYEE" ,"MANAGER" ))"Susan" ).password("test123" ).roles("EMPLOYEE" ,"ADMIN" ));
Authorize Access to Roles 1 2 3 4 5 6 http.authorizeRequests()"/" ).hasRole("EMPLOYEE" )"/leader/**" ).hasRole("MANAGER" )"/system/**" ).hasRole("ADMIN" )"/access-denied" );
Create the Access-Denied Page 1 2 3 4 5 6 7 8 9 <body>
Display Content Based on Roles 1 2 3 4 5 6 7 8 9 10 11 <security:authorize access="hasRole('MANAGER')" >"hasRole('ADMIN')" >
Add JDBC Database Authentication Run SQL Scripts 1 2 3 4 5 INSERT  INTO  `users` VALUES  'john' ,'{noop}test123' ,1 ),'mary' ,'{noop}test123' ,1 ),'susan' ,'{noop}test123' ,1 );
Note:  noop is the encoding algorithm of the password.
Add Database Support to pom.xml 1 2 3 4 5 6 7 8 9 10 11 <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.45</version > </dependency > <dependency > <groupId > com.mchange</groupId > <artifactId > c3p0</artifactId > <version > 0.9.5.2</version > </dependency > 
Create JDBC Properties File src/main/resources/persistence-mysql.properties
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 jdbc.driver =com.mysql.jdbc.Driver jdbc.url =jdbc:mysql://localhost:3306/spring_security_demo_plaintext?useSSL=false&serverTimezone=UTC jdbc.user =springstudent jdbc.password =springstudent connection.pool.initialPoolSize =5 connection.pool.minPoolSize =5 connection.pool.maxPoolSize =20 connection.pool.maxIdleTime =3000 
Define DataSource in Spring Config Introduce the Environment variable, this will hold data from the properties file. 
Add the @Autowired annotation.
1 2 3 4 5 6 @Autowired private  Environment env; private  Logger logger = Logger.getLogger(getClass().getName());
Define a Bean for the security data source.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 @Bean public  DataSource securityDataSource () try  {new  ComboPooledDataSource();catch  (Exception e){throw  new  RuntimeException(e);try  {"jdbc.driver" ));catch  (PropertyVetoException e) {throw  new  RuntimeException(e);">>> jdbc.url=" +env.getProperty("jdbc.url" ));">>> jdbc.user=" +env.getProperty("jdbc.user" ));"jdbc.url" ));"jdbc.user" ));"jdbc.password" ));"connection.pool.initialPoolSize" ));"connection.pool.minPoolSize" ));"connection.pool.maxPoolSize" ));"connection.pool.maxIdleTime" ));return  securityDataSource;
Define a helper method to read the env properties and convert them to integer.
1 2 3 4 5 6 7 private  int  getIntProperties (String propsName) int  intPropVal = Integer.parseInt(propVal);return  intPropVal;
Add JDBC Configuration Retrieve a securityDataSource Bean from the factory and inject into DemoSecurityConfig.java
1 2 3 4 5 6 7 @Autowired private  DataSource securityDataSource;@Override protected  void  configure (AuthenticationManagerBuilder auth)  throws  Exception 
Diagram 
Password Encryption Run SQL Scripts Change the DDL to Bcrypt hashing.
1 2 3 4 5 6 7 DROP  TABLE  IF EXISTS  `users`;CREATE  TABLE  `users` (varchar (50 ) NOT  NULL ,char (68 ) NOT  NULL ,1 ) NOT  NULL ,PRIMARY  KEY  (`username`)= InnoDB DEFAULT  CHARSET= latin1;
Password must be 68  chars.
{bcrypt} - 8 chars.{encodedPassword} - 60 chars. 
Modify db Properties 1 jdbc.url =jdbc:mysql://localhost:3306/spring_security_demo_bcrypt?useSSL=false&serverTimezone=UTC 
Behind the Scenes 
Retrieve the password from db for the user 
Read the encoding algorithm id 
For case of bcrypt, encrypt the plaintext password from the login form 
Compare the encrypted password from login from WITH encrypted password from db. 
If there is a matc, then login successful. 
 
Note:  The password from db is NEVER  decrypted. Because Bcrypt  is a one way  encryption algorithm.