Udacity
The Lesson Outline
Lesson Outline
ORM is the Norm: We introduce ORM, or object-relational-mapping, a software pattern that leverages the similarities between Java classes and SQL tables to eliminate boilerplate in data access code.
MyBatis Mappers: We introduce MyBatis, a dead-simple ORM tool for Java that integrates well with Spring. We discuss the "Mapper" classes MyBatis wants us to design to access the database.
Practical Example - User Credentials and Authentication: As a motivating example for using ORM, we discuss how to implement basic login security with a User table, MyBatis, and Spring Security. We walk through a lengthy sample project that implements the entirety of this motivating example.
Shannon Note
ORM : Object Relational Mapping, 這是一個方式去思考SQL table rows as Java Classes和個別的物件,你會學到如何去設計Java Classes去match data.簡單來說它是一個幫助使用者更簡單 安全的去從資料庫讀取資料,它的特性為透過程式語言去操作資料庫語言
ORM優點:
- 安全性: 可以避免SQL injection
- 簡化性: 他可以簡化SQL語法
- 通用性: 未來如果有資料庫轉移的問題,比較不會遇到要改寫程式的狀況
ORM缺點:
- 效能: 因為多了把程式語言轉譯成SQL語言
- 學習曲線高: 對初學者來說需要學SQL語法跟程式語言
- 複雜查詢維護性低: 就可能會用到原生地SQL語法
Concurrence: two or more events or circumstances happening or existing at the same time.
資料參考: 資料庫設計概念 - ORM https://ithelp.ithome.com.tw/articles/10207752
The Growing Layers of Our Application
Adding a database to our application is a way to externalize data persistence problems. When storing data in memory at runtime, we struggle to deal with:
Using a database allows us to isolate these concerns from the rest of our application, so we can focus on the business logic of our application.
There are many ways to manage the communication between an application and a database. For this course, we'll be using the library MyBatis to transform Java objects to SQL query parameters and to transform SQL query results into Java objects.
We'll create MyBatis Mappers as Spring beans, so we can inject them into any other beans that need them. For example, if we think about an online-shopping example, we might have a UserMapper that manages credentials and profile information and a CartMapper that manages the contents of an individual user's cart. We can inject the UserMapper into a Checkout Service that also receives the CartMapper to apply the charges in a User's cart to that User's stored payment information.
Later in this lesson, we'll combine our MyBatis mappers with Spring Security to authenticate each user's session automatically. To continue our earlier example, this means we could inject the UserMapper in some kind of Authentication Service to check client credentials on login.
Shannon Note
JDBC : Java DataBase Connectivity
- 跟IoC搭配起來不好用
MyBatis: 可以自動地把object轉換到SQL Query Parameters and SQL query Results back into objects.像是insert into users values到Database,又或是透過select * from來取得object裡面的內容
- Onion Archetecture
* 第一層External Request
* 第二層Controller
* 第三層Services
* 第四層Mappers: 主要用object class去代表table的data,然後透過object mapper class去寫query。- User Login with Spring Security
* 我們可以透過Spring Security去連接Mapper, 我們就可以告訴透過存在Database裡面的存證去檢查login。
* Spring Security可以自動追蹤users也可以給予user權限
Developing Your Intuition About ORM and Security
What Data Should be Stored in a Database?
How Should Data be Structured?
Thinking about Security
The main question to ask is: “What pages can a user access?”
The first step in using ORM is to define the data model. Consider the relationship between:
We can represent their relationship in SQL with this image below.
Relationship Between SQL Tables
A primary feature of ORM is that this type of relationship should have a natural mapping to Java classes. We can represent this same data in Java using a simple class diagram.
Class Diagram Corresponding to SQL Tables
The data types of these class attributes correspond to the data types of the SQL columns. Some Java types can be mapped to many different SQL types, and some SQL types can be mapped to multiple Java types, but in this case the type mappings are obvious. For a full list of the MyBatis type mapping, consult the MyBatis 3 TypeHandlers list.
Once you have defined your data types, MyBatis can automatically map Java objects to SQL statements.
ORM Process Visualization
Key Terms
Welcome to big business! You’ve been hired to implement the data model for a Taco Delivery service. Yum!
The diagram below depicts a relationship between customers, tacos, and deliveries. Each Customer
can have as many Order
s as they want. Each Order
must have one or more entries for TacoOrder
. The TacoOrder
entries have a name, price, and count. Lastly, you can optionally schedule each Order
for a Delivery
.
Your job is to make a class for each of these tables. There should be a Customer
, Order
, TacoOrder
, and Delivery
class. The variables in the classes should match the variables in the tables.
Taco Delivery Database Diagram
You should have created four classes for this exercise: Customer
, Order
, TacoOrder
, and Delivery
. These class files should live in a new data
folder in the course1
folder. The variables in each class should correspond to the variables in the database tables. Here are some sample implementations:
Customer.java
Order.java
TacoOrder.java
Delivery.java
MyBatis provides a shallow ORM layer over JDBC (Java Database Connectivity). That means it helps map your Java objects to queries that save and retrieve data using JDBC.
What is ORM(Object Relational Mapping?
MyBatis is mostly used through interface definitions. MyBatis automatically generates classes that implement the interface and makes them available as Spring beans. This is an example interface that defines a MyBatis Mapper.
補充: https://codertw.com/程式語言/410426/
This code uses #{username}
to identify the username parameter. It's like Thymeleaf parameters, but for SQL!
For more information on the template syntax MyBatis uses for SQL, check out mybatis official documentation.
mybatis documentation click here
There are additional annotations for @Insert, @Update, and @Delete as well.
See the further research section below the next video for more info on ways to configure MyBatis.
The @Insert
annotation automatically references attributes on the user object. Note username, firstName, lastName are all referenced directly here, because they are attributes of the user object.
This example also demonstrates the @Options
annotation. @Insert
normally returns an integer that is the count of rows affected. By using the @Options
annotation, we're telling MyBatis that we want to automatically generate a new key and put it in userId. Now the method will return the new userId once the row has been inserted.
All we have to do to use these methods is inject beans for this interface into our services and MyBatis will automatically create the code for the JDBC requests!
MyBatis Mappers Lie at the Center of Out Onion Architecture
Key Terms
@Select
, @Insert
, @Update
, @Delete
: Annotations representing SQL statements to be executed. Each annotation takes a string for a SQL statement of the corresponding type. For example, a @Select
annotation takes a string for a SQL SELECT statement.@Options
: Annotation providing access to switches and configuration options for JDBC statements.Further Research
Exercise: MyBatis Mappers
Earlier in this lesson we created some classes to help support our burgeoning Taco delivery business. Let’s create a @Mapper
interface that we can use to create new deliveries once our customer has successfully placed an order. As a reminder, here’s our Delivery class:
You’ll be presented with the DeliveryMapper interface, and it’s your job to declare methods to SELECT, INSERT, and DELETE Delivery records. Annotate each method with the appropriate annotations to perform the Select, Insert, and Delete operations as described here:
findDelivery
(or select
) method should take the id
of our Delivery
class and return the corresponding Delivery object.insert
method should take a Delivery
object and create a new record with the orderId
and time
. It should generate a new id
and return that id
.delete
method should take an id
and delete that record from the database. It doesn’t need to return anything.We created three new methods in our interface. In this solution they’re named findDelivery
, insert
, and delete
, but they can be named anything.
The first annotation is a simple @Select
like we saw earlier in the lesson, and the second annotation is pretty similar to the earlier example as well.
It uses INSERT
to create a new row in the Delivery table. Note that it only needs to provide the orderId and time values, because the id itself is generated. The @Options
annotation indicates to generate the key for the id
property and return it from the method. Also note that our VALUES
portion of the query just provides orderId and time directly. MyBatis can figure out that they are attributes of our Delivery
object.
Lastly, @Delete
is very similar to the @Select
, but make sure you use the right annotation for it!
Shannon Note
如果id是自己產生的,有設定@Option,在insert的時候就不需寫ID Value,寫了@option就需要寫一個方法來收到新增的delivery id。
如果使用@Delete他的回傳值是void,透過輸入id來執行@delete
Practical Example: User Credentials and Authentication
User support is a common feature in web applications, which means that a user can register an account and use credentials to login to the application in the future.
It's important to design databases with the assumption that they will someday be breached, and so we cannot store passwords or other secret credentials in plain text. Two approaches to storing passwords are:
Hashing and Encryption should occur in a service dedicated to that purpose, rather than on the front end or in the controller. Remember separation of concerns and our onion architecture! The idea is that all user flows originate externally, travel through a controller, then through one or more services, finally through a data access bean to the database, and then all the way back up the chain. Structuring applications this way makes it easy to follow dependencies and separate concerns, so that's how we're going to build applications from now on.
For a more in-depth discussion of salting and hashing passwords, see the further research section below the videos for this section.
For the full lecture sample code from this video and the next, check here
Below is an example of how to hash user passwords in the database. First, we have the User class and the UserMapper. When our UserService creates a new user, it uses a hashing service to convert the password to a hashed value before saving it.
User.java
UserMapper.java
Method in UserService.java
The hashing service itself has a single method that takes some data and salt and creates a string representing the hashed value.
Method in HashService.java
When a user logs in, we have no way to retrieve their original password, but we can re-hash their user input and see if it matches the hashed value in our database. Below is an example AuthenticationService that implements the AuthenticationProvider class. This allows Spring to integrate our provider with many different authentication schemes, but we can see in our supports
method that we specify that we only support UsernamePasswordAuthentication.
The authentication
method takes an Authentication object from spring and returns an authentication token if the user's credentials are correct.
AuthenticationService.java
For the full lecture sample code from the past two videos, check here
In order for Spring to actually use our AuthenticationService
, we need to extend our Web Security configuration. We do that with an adapter for the WebSecurityConfigurer
. This example overrides two configure methods:
AuthenticationService
to check user loginsHttpSecurity object
by chaining methods to express security requirementsSecurityConfig.java
We can see that the second configure
method does four things:
To see the HashService class used in this example, click here.
Key Terms
Further Research
Shannon Note
- plan text 明文 encrypted text 密文
Two apporaches to storing passwords are:- Encryption: 加密後可以解密回來
- Hashing: 加密後不可以解密回來
UserService.java
介紹
UserService主要目的是在檢查這位使用者是否為本人,然後因為implements >AuthenticationProvider
,所以需要實作兩個方法*
authenticate
* Spring Security確認收到的憑證,憑證通常來自form,然後要告訴authenticate
會接收到一個Authentication
的object然後回傳相同的類,Spring期望我們去讀取authentication object裡面的來自input的credential去確認她是valid。如果確定是本人就可以login,拿到權限Support
* 告訴Spring可以處理那些authenticationSecurityConfig.java
- @Configuration : Spring will use this as a source of configuration for the IoC context.
- @EnableWebSecurity : 讓Spring know that this class secifically is configuring Spring Security.
- 他繼承Abstract class
WebSecurityConfigurerAdapter
* 主要是讓我們去寫一個Adaptere給Spring WebSecurityConfigurer
* 實作兩個方法 different types ofconfigure
method
* 第一個configure
method
* 參數: AuthenticationManagerBuilder
* 內容: 透過AuthenticationManagerBuilder(Builder)上的方法來添加一個configure parts of the authentication scheme for our app
* 第二個configure
method
* 他定義Spring如何接收authorization要求SecureRandom:https://www.cnblogs.com/deng-cc/p/8064481.html
Exercise: User Credentials and Authentication
Our Taco Delivery service has been getting bombarded with fake orders, so it’s time to make sure only authenticated users can submit orders. Here’s our SecurityConfig
class with a configure
method already defined.
Your job is to fill in the method so it fulfills the following goals:
Here is an example configure
method that meets our requirements:
The first method chain permits all requests to the /order
page or to our css
and js
directories, and then it allows authenticated users to make any kind of request. The next chain permits all users to access the auto-generated login page at /login
. Remember that Spring creates this for us. Lastly, the third method chain redirects successful logins to the /tacos
page by default.
Exercise: Final Review
For this final review, you'll be updating your chat app from the last lesson to support user registration and login, as well as to store chat messages in a database rather than the in-memory list we created last lesson. That means you're going to have do a couple of things:
Re-enable Spring Security. Last lesson, we commented out the Spring Security dependencies in the project pom.xml file. This time we need to add it back! As a reminder, these are the two dependencies you need to un-comment (or add back to your project if you deleted them last time):
Define the database tables for the app. But wait, you say, didn't you tell us that we didn't have to know how to design SQL tables? That's right, I did! You won't have to design the tables yourself, but you do have to add the definitions I give you to your project. Specifically, you have to create a schema.sql
file in the src/main/resources
directory of your project. Here's the code you need to put in that file:
We haven't talked about this file much, but it's been in all of the examples we've looked at this lesson. Spring Boot will, by default, look for this file when the server starts and execute it if it's present. You can use this behavior for a variety of useful database-related tasks, but all we're using it for here is to create two tables in the database - USERS
and MESSAGES
.
Create Java classes that match the database tables. If we want to take advantage of MyBatis, we need to create a User
class that matches the data in the USERS
table above, and we need to update the ChatMessage
class from last lesson's final review to match the MESSAGES
table above. For the latter, you only need to add a single field, but I'll let you figure out which one.
Create MyBatis mapper classes to handle interacting with the database. As we discussed this lesson, we need to create specialized @Mapper
-annotated interfaces to access our database tables. You'll need two for this project - one for the USERS
table and one for the MESSAGES
table.
Add user registration and login support to the application, and require a login to access the /chat
URL. For this, I recommend looking at the example code from the practical example earlier in the lesson. Here's the link to the full project for reference. You'll have to implement the following elements from that example in this project as well:
AuthenticationService
, HashService
, and UserService
classesLoginController
and SignupController
classesSecurityConfig
classlogin.html
and signup.html
HTML templates/css
and /js
folders found in src/main/resources/static
.Most of these can be copied almost verbatim, but it's good practice for you to check and make sure you understand what needs to be copied, what needs to be changed, and why.
Update the MessageService
to use the database instead of an in-memory list of messages. That means it's going to have to use the MessageMapper
you created!
Update the chat.html
template to remove the username input from the message submission form. If we want the user to log in before seeing the chat page, we shouldn't make them enter their own username. Plus, we don't want to have users impersonating each other!
Update the ChatController
to get the currently-logged-in user's username upon message submission. You'll have to do some research for this one! It's common to need the currently-logged-in user's credentials when processing requests, so the solution shouldn't be too hard to find. You also need to attach the username to the message before passing it off to the UserService
- but again, that's up to you to figure out.
[BONUS] add a logout button to the chat template. It may not seem incredibly important right now, but generally it's actually very necessary to allow users to log out from an application. Many people across the world browse the internet from public computers in libraries and internet cafes, and even more share the same computer with friends and family. Our security implementation won't be worth much if the next person who uses the app continues to use the previous person's account! For this bonus task, you'll again have to do some research, this time into how Spring Security handles logout actions. I believe in you!
And that's it for this final review! I know these instructions might seem intimidating - but once you're actually implementing them, I think you'll find that it's less work than it seems. Good luck!
Final Review Exercise
It's time to add data persistence and security to our chat application! Update your previous final review project according to the tasks below, and refer back to the instructions above for the high-level goals and code snippets to include in your project.
schema.sql
file in src/main/resources
and add the USERS
and MESSAGES
table definitions from the instructions above./chat
URL to logged-in users.MessageService
class to use the mappers you created to add and retrieve messages to and from the database instead of an in-memory list.chat/html
template to remove the username input field from the message submission from.ChatController
to retrieve the currently-logged-in user's username from Spring Security when handling a message submissionExercise: Final Review
For this final review, you'll be updating your chat app from the last lesson to support user registration and login, as well as to store chat messages in a database rather than the in-memory list we created last lesson. That means you're going to have do a couple of things:
Re-enable Spring Security. Last lesson, we commented out the Spring Security dependencies in the project pom.xml
file. This time we need to add it back! As a reminder, these are the two dependencies you need to un-comment (or add back to your project if you deleted them last time):
Define the database tables for the app. But wait, you say, didn't you tell us that we didn't have to know how to design SQL tables? That's right, I did! You won't have to design the tables yourself, but you do have to add the definitions I give you to your project. Specifically, you have to create a schema.sql
file in the src/main/resources
directory of your project. Here's the code you need to put in that file:
We haven't talked about this file much, but it's been in all of the examples we've looked at this lesson. Spring Boot will, by default, look for this file when the server starts and execute it if it's present. You can use this behavior for a variety of useful database-related tasks, but all we're using it for here is to create two tables in the database - USERS
and MESSAGES
.
Create Java classes that match the database tables. If we want to take advantage of MyBatis, we need to create a User
class that matches the data in the USERS
table above, and we need to update the ChatMessage
class from last lesson's final review to match the MESSAGES
table above. For the latter, you only need to add a single field, but I'll let you figure out which one.
Create MyBatis mapper classes to handle interacting with the database. As we discussed this lesson, we need to create specialized @Mapper
-annotated interfaces to access our database tables. You'll need two for this project - one for the USERS
table and one for the MESSAGES
table.
Add user registration and login support to the application, and require a login to access the /chat URL. For this, I recommend looking at the example code from the practical example earlier in the lesson. Here's the link to the full project for reference. You'll have to implement the following elements from that example in this project as well:
AuthenticationService
, HashService
, and UserService
classesLoginController
and SignupController
classesSecurityConfig
classlogin.html
and signup.html
HTML templates/css
and /js
folders found in src/main/resources/static
.Most of these can be copied almost verbatim, but it's good practice for you to check and make sure you understand what needs to be copied, what needs to be changed, and why.
Update the MessageService to use the database instead of an in-memory list of messages. That means it's going to have to use the MessageMapper you created!
Update the chat.html
template to remove the username input from the message submission form. If we want the user to log in before seeing the chat page, we shouldn't make them enter their own username. Plus, we don't want to have users impersonating each other!
Update the ChatController
to get the currently-logged-in user's username upon message submission. You'll have to do some research for this one! It's common to need the currently-logged-in user's credentials when processing requests, so the solution shouldn't be too hard to find. You also need to attach the username to the message before passing it off to the UserService
- but again, that's up to you to figure out.
[BONUS] add a logout button to the chat template. It may not seem incredibly important right now, but generally it's actually very necessary to allow users to log out from an application. Many people across the world browse the internet from public computers in libraries and internet cafes, and even more share the same computer with friends and family. Our security implementation won't be worth much if the next person who uses the app continues to use the previous person's account! For this bonus task, you'll again have to do some research, this time into how Spring Security handles logout actions. I believe in you!
Shannon Note
小提示
* https://stackoverflow.com/questions/22557741/logout-link-with-spring-and-thymeleaf
* https://docs.spring.io/spring-security/site/docs/3.2.0.RC2/apidocs/org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.html#logoutUrl(java.lang.String)
* https://www.baeldung.com/spring-security-logout
And that's it for this final review! I know these instructions might seem intimidating - but once you're actually implementing them, I think you'll find that it's less work than it seems. Good luck!
For the full solution code, check here.
Glossary
@Select
annotation takes a string for a SQL SELECT
statement.
This lesson covered a lot! First we covered persistent data and learned:
We also took a peek at Security!
We’ve also taken our chat program to the next level by adding persistent storage and authentication to it!