- Compress images before upload
- Implement "send a copy of this to my email"
- Facebook/google login
- .and().requiresChannel().anyRequest().requiresSecure();
- Use a content security policy for Spring Boot XSS protection
- After login, always redirect to previous page
- Pagination
- Problem: When an admin deletes an account, the user still remains logged in
- Fix: On each request, check if the user exists in the db. If not, force logout the user and invalidate the session https://stackoverflow.com/a/38295610/18902234
- Secure end points with @PreAuthorize
- Add logging to all Service classes
-
Anything between two square bracket is valid thymeleaf syntax.
[[${userEmail}]] -
Show content to users that are authenticated/logged-in:
sec:authorize="isAuthenticated()". Add!to reverse -
Show content to users that have the role of
ADMIN:sec:authorize="hasRole('ROLE_ADMIN')" -
Use
${#authentication.principal.firstName}to get the firstName of the currently logged-in user. Make sure to usesec:authorize="isAuthenticated()to make sure the person seeing it is actually logged. Intellij will complain aboutfirstNamenot found -
To construct texts:
th:text="|Hello, ${#authentication.principal.firstName}|" -
To construct links:
th:href="@{|/profile/${#authentication.principal.accountId}|}" -
th:fieldoverridesth:id,th:name, andth:value -
-
To pass only string from view to controller. Name attribute is important:
<form th:action="@{/process}" method="post"> <input type="text" name="stringName"/> <input type="submit" /> </form> -
And in the controller:
@PostMapping("/process") public String process(@RequestParam String stringName) { return "hehe"; }
-
-
To show messages in url without Request params, using SessionAttributes:
-
In the Controller, create a method to return a HashMap (or anything really) and annotate with
@ModelAttribute("customName")like:@ModelAttribute("customName") public HashMap<String, Boolean> customName() { return new HashMap<>(); }The code above allows that instance of HashMap to be used in this model
-
Annotate the controller class with
@SessionAttribute("customName"). The annotation tells spring to treatcustomNameas a session attribute. -
To use in a mapping, add it to the parameters. E.g:
@PostMapping("/url") public String updateAccount(@ModelAttribute("customName") HashMap<String, Boolean> message) { // update, delete and do what you want here. }The value of customName will be stored in
message. -
Anything that is stored in
message, either in the above Mapping or in any Mapping (that has the parameters) - will be available for use in anywhere that uses this controller -
To use in thymeleaf, assuming customName was message and one of the key value pair is in form
<String, Boolean>:<div class="w-50" th:if="${message.get('updateStatus') == true}"> <div class="alert alert-success">Account updated successfully</div> </div> <div class="w-50" th:if="${message.get('updateStatus') == false}"> <div class="alert alert-danger">Update failed, try again</div> </div> <th:block th:if="${message.remove('updateStatus')}"></th:block>If the key is not removed, it will show everytime.
-
To use the attribute globally, in all models/views, create a ControllerAdvice class like:
@ControllerAdvice public class GlobalControllerAdvice { @ModelAttribute("message") public HashMap<String, Boolean> message() { return new HashMap<>(1); } }And remove the method that was declared in Step 1
-
Don't forget to mark the Controller class you want to use it with. Refer to step 2
-
-
When there's a table, and inside the table, there's a button. The button triggers a modal to be opened. To send data from button click to modal, use jquery, like in
admin-view-account.htmlandscript.js-
Add your modal normally. Give it and id, e.g
modalId. Disable the default bootstrap modal opener by removing the bootstrap attributes from the<a>or<button>tag. Give the tag the link to fetch the data from:<a th:href="@{|/admin/findId/${account.accountId}|}" class="btn btn-primary">A hehe button</a>The href will be used to make a get request in jquery. The result of the get request will be sent to the modal
-
In script file, using jquery, assuming id of the modal is
modalId:$('document').ready(function () { $('.table .btn').on('click', function (event) { // on click of table or/and button event.preventDefault(); // prevents the link from opening const href = $(this).attr('href');// get the href assosicated with that button $.get(href, function (account) {// make a get request, response will be stored in `account` $('#firstName').val(account.firstName);// store the value of account.firstName in the field with id firstName $('#lastName').val(account.lastName);// same as above }); $('#modalId').modal('show') // show the modal }) }) -
And in
AdminController:@GetMapping("/findId/{id}") @ResponseBody public HashMap<String, String> getById(@PathVariable Long id) { Account account = accountService.findByAccountId(id); HashMap<String, String> accountInfo = new HashMap<>(2); accountInfo.put("firstName", account.getFirstName()); accountInfo.put("lastName", account.getLastName()); return accountInfo; }@ResponseBodyis used soaccountInfocan be serialized back into Mr JSON. -
They key of the hashmap, the
nameattribute given to the input field and the "key" in.val(account.firstName);must be the same. E.g. For firstName to be filled properly, following the jquery and GetMapping- The key in accountInfo should be
firstName. The name attribute in the input tag where it would be filled should also befirstName- ID can be anything, I just used firstName because copy and paste is easy
- The key in accountInfo should be
-
-
Change text of a button:
$('#buttonId').html('New Text') -
Change form action:
$('#formId').attr('action', 'NewActionUrl');
-
Records automatically create private final fields, getters, AllArgsConstructor, toString, equals and hashcode
-
Records can have methods, including static methods
-
Static fields are also allowed. Instance variables are not allowed
-
Records can implement interfaces
-
Get the url of the website.
https://www.title.comreturns well,https://www.title.comString websiteUrl = ServletUriComponentsBuilder.fromRequestUri(request) .replacePath(null) .build() .toUriString(); -
To check if a user is Authenticated:
Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (!(auth instanceof AnonymousAuthenticationToken)) { // user is authenticated Account authAccount = (Account) auth.getPrincipal(); userFirstName = authAccount.getFirstName(); } -
Flash Attributes will be removed immediately the redirected page has been rendered
-
Run
heroku create <websiteName> -
Run
heroku addons:create cleardb:ignite -a websiteName. cleardb: ignite makes mysql supported. Ignite specifies free version -
Run the website in
localhost, so the mysql database will be created. Make sure all the tables have been created. -
Export the local database by running
mysqldump -u <username> -p <databaseName> > nameOfExportFile.sql.- Password will be required, you might see a warning about entering password in terminal. The exported files will be used later.
-
Run
heroku run -a websiteName printenvto see all environment variables. Note the JDBC variables:DATABASE_URL,DATABASE_USERNAME, andDATABASE_PASSWORD. It will be used in gaining remote access below. -
Gain remote access into the heroku database by running
mysql -u <username> -p<password> -h <dataSourceUrl>.-
E.g, if your username is
b69c69d69, and password isss69ss69. For database URL, only use link from aftermysql://up to.net. E.gus-cdbr-west-02.cleardb.net. -
What you will run is:
mysql -u b69c69d69 -pss69ss69 -h us-cdbr-west-02.cleardb.net. Note that there's no space between the -p in password
-
-
At this point, I should be in mysql mode, connect to the database by running
connect <databaseName>.- Find
databaseNameby runningSHOW DATABASES;. It will be in form ofheroku_1s232dsf23232
- Find
-
Run
select @@character_set_database, @@collation_database;.- Below, I'm assuming I got
utf8forcharacter_set_databaseandutf8_general_ciforcollation_database
- Below, I'm assuming I got
-
Open the sql file you exported earlier. Set the database with
USE <databaseName>;before any Sql. Find and replace all values of CHARSET and COLLATE. E.g:-
Find
CHARSET=utf8mb4and replace withCHARSET=utf8. -
Also, find
COLLATE=utf8mb4_0900_ai_ciand replace withCOLLATE=utf8_general_ci
-
-
Import the .sql file into the remote database by running (not in sql model):
mysql -u <username> -p<password> -h <datasourceUrl> < <pathOfExportedDb>. You should get a password warning -
Verify the shit has been installed. Connect to remote database again (#6, #7) , run
SHOW TABLES;. The tables shown should be the same as what I had in the local database
-
Login to heroku on website. To your website dashboard, under deploy, choose GitHub and link the repo/branch you want.
-
In settings -> Config vars,
CLEARDB_DATABASE_URLis there by default. -
SPRING_PROFILES_ACTIVEto specify which.propertiesfile to use. If I want to use aapplication-test.propertiesfile, the value ofSPRING_PROFILES_ACTIVEwill betest. -
Environment variables can also be set from here. Use in .properties file as
${VARIABLE_NAME} -
To use
application-dev.propertiesfile inlocalhost, top kinda right of screen -> edit configurations -> modify options -> make sire Add VM options is checked -> in space for VM Options:-Dspring.profiles.active=dev -
To set localhost environment variable: same as above, just make sure Environment Variables is checked
- Environment variables from heroku have a higher priority that .
propertiesfile, so no need to updateusernameandpasswordin .propertiesfile