- 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 aboutfirstName
not found -
To construct texts:
th:text="|Hello, ${#authentication.principal.firstName}|"
-
To construct links:
th:href="@{|/profile/${#authentication.principal.accountId}|}"
-
th:field
overridesth: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 treatcustomName
as 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.html
andscript.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; }
@ResponseBody
is used soaccountInfo
can be serialized back into Mr JSON. -
They key of the hashmap, the
name
attribute 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.com
returns well,https://www.title.com
String 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 printenv
to 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
databaseName
by runningSHOW DATABASES;
. It will be in form ofheroku_1s232dsf23232
- Find
-
Run
select @@character_set_database, @@collation_database;
.- Below, I'm assuming I got
utf8
forcharacter_set_database
andutf8_general_ci
forcollation_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=utf8mb4
and replace withCHARSET=utf8
. -
Also, find
COLLATE=utf8mb4_0900_ai_ci
and 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_URL
is there by default. -
SPRING_PROFILES_ACTIVE
to specify which.properties
file to use. If I want to use aapplication-test.properties
file, the value ofSPRING_PROFILES_ACTIVE
will betest
. -
Environment variables can also be set from here. Use in .properties file as
${VARIABLE_NAME}
-
To use
application-dev.properties
file 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 .
properties
file, so no need to updateusername
andpassword
in .properties
file