Feb 1997

Overtime and overdue


  • Home

  • Tags

  • Categories

  • Archives

  • Search

Spring REST 5: @PathVariable

Posted on 2020-07-25

Previously, we used @RestController to handle the REST request to get the list of students(http://localhost:8080/api/students).
Now, assume we have a list of students, and the client only wants the first one. So we need to retrieve a single student by id: api/getStudentById/{studentId}

Behind the scenes

  1. We’ll make a request across for /api/students/{studentId}
  2. We’ll have Spring REST along with Jackson and they’ll make a call to the REST service
  3. In our code, we’ll actually return that given student
  4. Jackson will actually convert that student object or that student POJO to JSON and then send it back across to the REST client

    Development Process

  • Add request mapping to Spring REST Service

    • Bind path variable to method parameter using @PathVariable
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
          @GetMapping("/getStudentById/{studentId}")
      <!-- the parameter should be the same as the content in the bracket(studentId) -->
      public Student getStudentById(@PathVariable int studentId) {
      List<Student> theStudents = new ArrayList<>();

      theStudents.add(new Student("Poornima","Patel"));
      theStudents.add(new Student("Mario","Rossi"));
      theStudents.add(new Student("Mary","Smith"));

      return theStudents.get(studentId);
      }
  • Test
    Postman:
    GET:http://localhost:8080/SpringRESTDemo_war_exploded/api/getStudentById/1

Spring REST 4: @RestController

Posted on 2020-07-25

@Restcontroller

  • Extension of @Controller
  • Handles REST requests and response

Development Process

Maven dependency for Spring MVC and Jackson

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- Add Jackson for JSON converters-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.0</version>
</dependency>

<!-- Add Spring MVC and REST support -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>

<!-- Add Servlet support for Spring's AbstracAnnotationConfigDispatcherServletInitializer -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>

All Java Config: @Configuration

1
2
3
4
5
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.xliu")
public class DemoAppConfig {
}

All Java Config: Servlet Initializer

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 null;
}

@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{DemoAppConfig.class};
}

@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}

Create Spring REST Service using @RestController

1
2
3
4
5
6
7
8
@RestController
@RequestMapping("/test")
public class DemoRestController {
@GetMapping("/hello")
public String sayHello(){
return "Hello!";
}
}

Address: http://localhost:8080/test/hello

Spring REST 3: REST over HTTP

Posted on 2020-07-25
  • Leverage HTTP methods for CRUD operations
    |Http Method|CRUD Operation|
    | —— | —— |
    |POST|Create a new entity|
    |GET|Read a list of entities or single entity|
    |PUT|Update an existing entity|
    |DELETE|Delete an existing entity|

HTTP Messages

Client seds HTTP Request Message to Server then Server sends HTTP Response Message to Client.

HTTP Request Message

  • Request line: the HTTP command
    • get method / post method
  • Header variables: request metadata
    • basic information of request
  • Message body: contents of message

    HTTP Response Message

  • Request line: server protocol and status code
    • 200/404/500
  • Header variables: response metadata
    • content type of data(xml/JSON/…)
  • Message body: contents of message

Spring REST 2: POJO JSON Conversion

Posted on 2020-07-25 Edited on 2020-08-10

Spring and Jackson Support

  • When building Spring REST applications
    • Spring will automatically handle Jackson Integration
    • JSON data being passed to REST controller is converted to POJO
    • Java object being returned from REST controller is converted to JSON

      Set up

      Maven

      1
      2
      3
      4
      5
      <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.5</version>
      </dependency>

Create Student POJO Java Class

1
2
3
4
5
6
7
8
9
10
11
12
public class Student {

private int id;
private String firstName;
private String lastName;
private boolean active;

public Student() {

}
// Getters and Setters
}

Java POJO to JSON

Basically we are using the method writeValue() of Jackson pacakge.

1
2
3
4
5
6
7
8
9
ObjectMapper objectMapper = new ObjectMapper();
Student student = new Student();
student.id = 1;
student.firstName = "Xiaoke";
student.lastName = "Liu";
// write in a file
objectMapper.writeValue( new FileOutputStream("data/output-2.json"), student);
// Convert to String
String json = objectMapper.writeValueAsString(student);

JSON to Java POJO

Basically we are using the method readValue() of Jackson pacakge.

  1. Read Object From JSON File

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public class Driver {

    public static void main(String[] args) {

    try {
    // create object mapper
    // ObjectMapper() from jackson.databind
    ObjectMapper mapper = new ObjectMapper();

    // read JSON file and map/convert to Java POJO:
    // data/sample-lite.json
    Student theStudent = mapper.readValue(
    new File("data/sample-lite.json"), Student.class);

    // print first name and last name
    System.out.println("First name = " + theStudent.getFirstName());
    System.out.println("Last name = " + theStudent.getLastName());

    }
    catch (Exception exc) {
    exc.printStackTrace();
    }
    }
    }
  2. Read Object From JSON Reader

    1
    2
    3
    4
    ObjectMapper objectMapper = new ObjectMapper();
    String studentJson = "{ \"id\" : \"001\", \"firstName\" : \"xiaoke\", \"lastName\" : \"Liu\" }";
    Reader reader = new StringReader(carJson);
    Student student = objectMapper.readValue(reader, Student.class);
  3. Read Object From JSON String

    1
    2
    3
    ObjectMapper objectMapper = new ObjectMapper();
    String studentJson = "{ \"id\" : \"001\", \"firstName\" : \"xiaoke\", \"lastName\" : \"Liu\" }";
    Student student = objectMapper.readValue(studentJson, Student.class);
  4. Read Object From JSON via URL

    1
    2
    3
    ObjectMapper objectMapper = new ObjectMapper();
    URL url = new URL("file:data/student.json");
    Student student = objectMapper.readValue(url, Student.class);

If JSON has property you don’t care about, which means that there is no corresponding field in POJO, use@JsonIgnoreProerties(ignoreUnknow=true) over POJO

Spring REST 1: JSON

Posted on 2020-07-25

What is JSON?

  • JavaScript Object Notation
  • Lightweight data format for storing and exchanging data
  • JSON is plain text data

JSON Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
// Numbers: no quotes
"id": 14,
// String: in double quotes
"firstName": "Mario",
"lastName": "Rossi",
// Boolean: true / false
"active": true
// Nested JSON object
"address": {
"steet": "100 Main St",
"city": "Philadelphia",
"state": "Pennsylvania",
"zip": "19103",
"country": "USA"
}
// Array: use square brackets[...]
"languages": ["Java","C#","Python","Javascript"]
// null
}

JSON Data Binding

  • Data binding is the process of converting JSON data to a Java POJO, also know as Mapping, Serialization/Deserialization, Marshalling/Unmarshalling

    JSON Data Binding with Jackson

  • Spring uses the Jackson Project behind the scenes
  • Jackson handles data binding between JSON and Java POJO
  • By default, Jackson will call appropriate getter/setter method
    • JSON to Java POJO
      • call setter methods on POJO(automatically)
    • Java POJO to JSON
      • call getter methods on POJO(automatically)

Spring REST 0: REST Web Service

Posted on 2020-07-25
  • REST: REpresentational State Transfer
  • It’s a lightweight approach for communicating between applications.
  • REST is language independent.
  • REST applications can use any data format
    • Commonly see XML and JSON(most popular and modern)

Spring Security 9: Password Encryption

Posted on 2020-07-25

bcrypt

  • Performs one-way encrypted hashing
  • Adds a random salt to the password for additaional protection
  • Includes support to defeat brute force attacks

Spring Security Password Storage

  • In Spring Security 5, passwords are stored using a specific format.
    • {bcrypt}encodedPassword
    • Password column must be at least 68 chars wide:
      • {bcrypt} - 8 chars
      • encodedPassword - 60 chars

Modify DDL for Password Field

1
2
3
4
5
6
7
8
CREATE TABLE `users` (
`username` varchar(50) NOT NULL,
-- Password column must be at least 68 chars wide:
`password` char(68) NOT NULL,
`enabled` tinyint(1) NOT NULL,

PRIMARY KEY(`username`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Spring Security Login Porcess

User enters plaintext password and go through Spring Security Filters

  1. Retrieve password from db for the user
  2. Read the encoding algorithm id(bcrypt etc)
  3. For case of bcrypt, encrypt plaintext password from login form(using salt from db password)
  4. Compare encrypted password from login form WITH encrypted password from db
  5. If there is a match, login successful
  6. If no match, login NOT successful
    Note: The password from db is NEVER decrypted, because bcrypt is a one-way encryption algorithm

Spring Security 8: JDBC

Posted on 2020-07-24 Edited on 2020-07-25

Till now, we are using the in-memory data for studying purpose.
DemoSecurityConfig.java:

1
2
3
4
5
6
7
8
9
10
    @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// add our users for in memory authentication
UserBuilder users = User.withDefaultPasswordEncoder();

auth.inMemoryAuthentication()
.withUser(users.username("john").password("test123").roles("EMPLOYEE"))
.withUser(users.username("mary").password("test123").roles("EMPLOYEE","MANAGER"))
.withUser(users.username("susan").password("test123").roles("EMPLOYEE","ADMIN"));
}

And now, we will try to add database access.

Database Support in Spring Security

  • Spring Security can read user account info from database
  • By default, you have to follow Spring Security’s predefined table schemas

    Development Process

  1. Develop SQL Script to set up databse tables
    Spring Security has a default database schema, so you need to provide two tables, one call users and one call authorities. And you have to use these exact table names, and also these tables need to have these columns: username, password and enabled for users, and authorities is the username and authority(authority is the same thing as roles).

    1
    2
    3
    4
    5
    6
    7
    CREATE TABLE `users` (
    `username` varchar(50) NOT NULL,
    `password` varchar(50) NOT NULL,
    `enabled` tinyint(1) NOT NULL,

    PRIMARY KEY (`username`)
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;

    In Spring Security 5, passwords are stored using a specific format:{id}encodedPassword

    | id | Description |
    | —— | —— |
    | noop | Plain text passwords |
    | bcrypt | BCrypt password hashing |

    1
    2
    3
    4
    5
    INSERT INTO `users`
    VALUES
    ('john','{noop}test123',1),
    ('mary','{noop}test123',1),
    ('susan','{noop}test123',1);

    Note: the password is “test123” while the {noop} let Spring Security know the passwords are stored as plain text{noop}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        CREATE TABLE `authorities` (
    `username` varchar(50) NOT NULL,
    `authority` varchar(50) NOT NULL,

    UNIQUE KEY `authorities_idx_1` (`username`,`authority`),

    CONSTRAINT `authorities_ibfk_1`
    FOREIGN KEY (`username`)
    REFERENCES `users`(`username`)
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
    1
    2
    3
    4
    5
    6
    7
    INSERT INTO `authorities`
    VALUES
    ('john','ROLE_EMPLOYEE'),
    ('mary','ROLE_EMPLOYEE'),
    ('mary','ROLE_MANAGER'),
    ('susan','ROLE_EMPLOYEE'),
    ('susan','ROLE_ADMIN');

    Note: Internally Spring Security uses “ROLE_” prefix

  2. Add database support to Maven POM file
    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>
  3. 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 connection properties
    #
    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/spring_security_demo_plaintext?useSSL=false
    jdbc.user=root
    jdbc.password=pwd

    #
    # Connection poll properties
    #
    connection.pool.initialPoolSize=5
    connection.pool.minPoolSize=5
    connection.pool.maxPoolSize=20
    connection.pool.maxIdleTime=3000
  4. Define DataSource in Spring Configuration
    DemoAppConfig.java:

    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
    35
    @Configuration
    @EnableWebMvc
    @ComponentScan(basePackages = "com.xliu.springsecurity.demo")
    // src/main/resources files are automatically copied to classpath during Mavan build
    @PropertySource("classpath:persistence-mysql.properties")
    public class DemoAppConfig {
    @Autowired
    private Environment env;

    private Logger logger = Logger.getLogger(getClass().getName());

    @Bean
    public DataSource securityDataSource() {
    // create connection poll
    ComboPooledDataSource securityDataSource = new ComboPooledDataSource();
    // set the jdbc driver
    try {
    securityDataSource.setDriverClass(env.getProperty("jdbc.driver"));
    } catch (PropertyVetoException e) {
    throw new RuntimeException(e);
    }
    logger.info(">>>> jdbc.url=" + env.getProperty("jdbc.url"));
    logger.info(">>>> jdbc.user=" + env.getProperty("jdbc.user"));
    // set database connection props
    securityDataSource.setJdbcUrl(env.getProperty("jdbc.url"));
    securityDataSource.setUser(env.getProperty("jdbc.user"));
    securityDataSource.setPassword(env.getProperty("jdbc.password"));
    // set connection pool props
    securityDataSource.setInitialPoolSize(Integer.parseInt(env.getProperty("connection.pool.initialPoolSize")));
    securityDataSource.setMinPoolSize(Integer.parseInt(env.getProperty("connection.pool.minPoolSize")));
    securityDataSource.setMaxPoolSize(Integer.parseInt(env.getProperty("connection.pool.maxPoolSize")));
    securityDataSource.setMaxIdleTime(Integer.parseInt(env.getProperty("connection.pool.maxIdleTime")));

    return securityDataSource;
    }

    Environment is a Spring helper class to hold the data that was read from the properties file

  5. Update Spring Security Configuration to use JDBC
    DemoSecurityConfig.java:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Configuration
    @EnableWebSecurity
    public class DemoSecurityConfig extends WebSecurityConfigurerAdapter {
    // Inject our data source that we just configured
    @Autowired
    private DataSource securityDataSource;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    // Tell Spring Security to use JDBC authentication with our data source
    auth.jdbcAuthentication().dataSource(securityDataSource);

Here we no longer use hard-coding users.

Spring Security 7: Spring Security JSP Tags

Posted on 2020-07-24

Sometimes we want to show specific content to different users based on their roles, like show system links to admins only. Spring Security provided JSP tags to solve this problem.
General syntax:

1
2
3
<security:authorize access="hasRole('MANAGER')">
<!-- content in this tag will show according to the authorization -->
</security:authorize>

home.jsp:

1
2
3
4
5
6
7
8
9
10
11
12
<security:authorize access="hasRole('MANAGER')">
<p>
<a href="${pageContext.request.contextPath}/leaders">LeaderShip Meeting</a>
(Only for Manager peeps)
</p>
</security:authorize>
<security:authorize access="hasRole('ADMIN')">
<p>
<a href="${pageContext.request.contextPath}/admins">LeaderShip Meeting</a>
(Only for Admin peeps)
</p>
</security:authorize>

Spring Security 6: Customize Access Denied Page

Posted on 2020-07-24

Since we don’t want the default access denied page, we can create a customized one.

Update DemoSecurityConfig

DemoSecurityConfig.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").hasRole("EMPLOYEE")
.antMatchers("/leaders/**").hasRole("MANAGER")
.antMatchers("/admins/**").hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/showMyLoginPage")
.loginProcessingUrl("/authenticateTheUser")
.permitAll()
.and()
.logout()
.permitAll()
// new added
.and()
// "/access-denied" needs a new controller
.exceptionHandling().accessDeniedPage("/access-denied");
}

Update LoginController

LoginController.java:

1
2
3
4
@GetMapping("/access-denied")
public String accessDenied() {
return "access-denied";
}

Create page /access-denied

Skip

123…12
Feb 1997

Feb 1997

112 posts
4 categories
24 tags
© 2020 Feb 1997