搜尋

搜尋結果

馬可孛羅意大利美食漫遊
澳城餐飲
文創遊樂場 // 米米 Esther Lim・2017-05-30

聚所周知,意大利人懂得享受美食,注重食材質量及講求烹調的技巧。金麗華酒店薈景閣咖啡室的海鮮自助晚餐由6月1日起,薈景閣咖啡室海鮮自助晚餐再有新鮮「煮」意,這次以意大利商人及旅行探險家馬可孛羅為靈感,主打龍蝦黑松露燴意大利飯和濃味意大利麵,再配上多款的西式冷盤、中式熱葷及甜點,帶您的味蕾享受穿越從西至東的饗宴。 意大利飯搭配嚴選的上乘龍蝦,以及矜貴的黑松露,領略意大利的北部濃濃美味的龍蝦黑松露燴意大利飯。 濃味意大利麵,除了以基本材料麵粉及蛋外,還有黃色的藏紅花、紅色的蕃茄及紅菜頭、綠色的菠菜及香草和黑色的墨魚等材料熬製成的汁液,製成意大利麵。煮熟的意大利麵會立即放入原個桶型的意大利芝士王者Parmigiano Reggiano,讓意大利麵的熱力融化濃郁牛乳味道的芝士。 另有中式燒味及現煮沙鍋白酒炒蜆的中式明檔,滋味倍增。除了上述主打菜式,一如既往大受歡迎的生蠔、龍蝦、海蝦等加上多款意國菜餚及中式美饌任君選擇,特式推介:自製比薩、巴馬芝士配馬鈴薯糰子蔬菜球、醬蒸全魚、翠綠鮮蝦蒜茸炒時蔬等。 金麗華享負盛名的甜點,是絶對不能錯過的一環。意大利杏仁脆餅、意式奶凍及意大利芝士餅Tiramisu,為整個美食漫遊劃上完美的句號。 馬可孛羅海鮮自助晚餐 逢星期四至日 晚上6時半至10時 成人 澳門幣 348元│小童  澳門幣 174元 6至18 歲 查詢請聯絡:853 8793 3871 ndash; 薈景閣咖啡室

時光隧道-綠庭吧酒吧
澳城餐飲
文創遊樂場 // 米米 Esther Lim・2017-03-17

位於麗景灣藝術酒店內的綠庭吧,與眾不同的是像電影時光隧道的酒吧一樣,寧靜優雅,清雅脫俗。 筆者特意為讀者朋友介紹綠庭吧,因為酒吧保持懷舊氣息,三五知己好友相約輕談淺酌的最佳地點,在酒吧飲杯美麗的飲品,點些有特色的小食,然後聽聽音樂看看日落看看夜景,何樂而不為?現在介紹一下部分特色飲品: 用新鮮薄荷葉、青檸、Bacardi Rum蘭姆酒釀成的Mojito,味道清甜,精神為之一振。 霧白色加上青檸檬片點綴的Frozen Lime Daiquiri,賣相漂亮。 顏色鮮豔加上一片菠蘿,加上少量酒精變成了的美麗熱帶飲品Singapore Sling。 綠庭吧酒吧特色小食: 印度香脆雜菜球:用料包括青尖椒、蘿蔔、洋蔥、茄瓜、椰菜、麵粉及印度香料等,味道配合一流。 咖喱角:用薯仔、青豆再加上自家製麵粉外皮,可口健康。 芝士薄餅配牛油果醬:新鮮洋蔥、檸檬汁及少許指天椒,再加上整個牛油果,釀成醬就是特別為這芝士薄餅而設。 綠庭吧酒吧除了上述飲品每款MOP50 15% 和小食,當然還有令人快樂的甜品:紐約芝士蛋糕、焦糖布甸、鮮果橙汁伴雪糕、草莓新地等。小食還有:炸馬介休球和雜錦芝士拼盤等,以及Pink Panther、Grasshopper、Screwdriver及Kamikaze等雞尾酒。 營業時間:12002400電話:853 8899 6861。 地址:澳門氹仔史伯泰海軍將軍馬路二號 麗景灣藝術酒店地下

渣男
玄學星相
熊神進・2016-11-21

八字不平衡, 太炎太寒都會左右一個人的婚姻, 她的官殺星混雜, 從某些角度來說, 女性的官星宜專, 所謂專, 就是不混殺星, 一當殺星出現, 戀愛關係就複雜起來, 未必是件樂事。 她一次又一次遇上渣男, 每個男人都是喜歡她的身體, 睡醒了, 他們又找一個理由分手, 她很累, 步入三十歲, 床是冷冷的。 她看到鄰家姑娘走入婚姻殿堂, 而自己呢, 她哭了很多次, 每次的心都是痛。 她嘗試用最有效的肥皂去清洗渣男留在自己身體上的負能量, 很痛心, 她愈用力去擦, 身體愈受傷, 她仍是帶著滿身負能量活下去 在玄學上, 女人遇到渣男是因為大運的五行先合夫宮再遇上ldquo;亡魂日rdquo;, 女人在亡魂日是最迷糊, 半昏半醒就失去了身體。玄學家認為, 渣, 是人品問題, 是五行太枯太潤的缺憾, 從過去的觀察, 筆者發現渣男是極度以自我為中心、極度自私自利、極擅長索取、極不會對女性負責任逼女友打胎。渣男的人格存在永久的缺陷, 但他不認為自己有什麼問題。很多女讀者來信向我哭訴, 她們妄想改變男友, 她們很累, 最終在長久日子裡接受他的變態心理, 認為他是帥有錢很愛自己, 筆者不是愛情專家, 只是從她們的人生中印證一句話 命運掌握在強者手裡, 如果她們一開始懂得放手, 今天她們就改變了命運 在婚姻輔導座談會中, 我記得自己曾經說過一句話之後被很多讀者轉發 情緒穩定對一個男人很重要, 翻臉比翻書還要快的男人, 他一定是浮雲, 不靠攏, 離開吧。

Last minute 飛台北!澳門航空 2人或以上同行優惠,每人來連稅MOP951!
激安優惠
OHChance 旅遊誌・2016-09-03

澳門航空少見地推出多人(2人或以上)同行優惠,是次台北航線專享: 最低價錢報告.9月1日至15日出發:MOP951每人.9月20日至30日出發:MOP1,051每人 以上為澳門出發飛台北 2人或以上同行每人價錢,價錢已包含稅項。  重點資訊.由於是多人同行優惠,故此搜價時記得選2人或以上方會找到平飛;.官方公告是2人同行優惠,但事實上只要平飛夠,直接搜3人、4人同行都會有優惠價;.平飛量不算多,而且購票系統的前後3天票價矩陣不會顯示多人同行優惠價,必需一天一天找;.以9月last minute 來說,唔使1000飛到台北真心正;.注意澳門飛台北必需選NX602(即8點45起飛的),回程則不限。  附加資訊.澳門航空票價已包機上餐飲及20KG 托運行李額。  附註:上述最低價錢為航空公司公告之最優惠價格,或本站能找到的最低價格;每一航班有否優惠票價及所存票量由航空公司決定,優惠票量有限售完即止。   【促銷公司】澳門航空(Air Macau)【搭乘日期】即日起至9月30日【販賣時間】已開賣,至9月29日2359【最長停留】7天【航班限制】去程限坐NX602【預訂網址】httpohchance.inforefairmacau  價錢 Sample ndash; 澳門飛台北2人同行總價MOP1,902

澳門百老匯打造澳門全新景點-「情繫百老匯」浪漫勝地於七夕情人節正式對遊客開放
小城角落
Benjamin・2016-08-12

「澳門百老匯trade;」於今日宣佈為澳門帶來又一觀光新景mdash;mdash;「情繫百老匯」情人鎖牆。 「情繫百老匯」將是全澳第一個見證並紀念情侶間甜蜜愛情的全新約會地點,勢必將成為各地旅客爭相拜訪的熱門景點。 「情繫百老匯」情人鎖牆坐落于澳門「百老匯美食街」內的「福龍葡國餐廳」對面,眺望著橫琴新區,鎖頭將愛侶之間的戀人絮語牢牢鎖在圍欄上,在寬闊的河景前互換誓言,象徵著堅定恒久的愛情。遊客們可選擇在現場購買情人鎖或自行攜帶鎖頭,在這個新興愛情紀念勝地留下難忘的浪漫符號。 澳門特別行政區政府旅遊局旅遊產品及活動廳代廳長司徒琳麗女士、澳門特別行政區政府旅遊局旅遊產品處處長葉穎珊女士與「澳門百老匯」總經理麥佩欣女士及銀河娛樂集團市場推廣事務首席市場推廣總監祁禮敦先生共同出席了「情繫百老匯」的啟動儀式,見證此次盛事。麥佩欣女士表示:ldquo;我們選擇在七夕情人節之際宣佈「情繫百老匯」的正式對外開放,不僅為遊客以及情侶們提供一個度過七夕佳節的完美去處,並且「情繫百老匯」作為澳門的又一新景點,將會持續地吸引更多遊客以及本地居民前來共締幸福回憶。rdquo; 「澳門百老匯」通過一座設計優美、備有空調裝置的行人天橋與世界一流的亞洲著名度假勝地「澳門銀河trade;」綜合渡假城緊緊相連。「澳門百老匯」提供地道澳門風情的度假體驗,遊客在此可盡享街頭美食以及街頭娛樂表演,在設有約3,000個座位的「百老匯舞台」欣賞到世界級的優秀表演。作為「澳門百老匯」的一部分,「百老匯酒店」是一所五星級精品酒店,賓客在飽覽沿岸壯麗景致的同時還可備貼心周到的專業服務。酒店共設有320間客房及套房可供選擇。

至愛新聽力,我想多謝...
娛樂殿堂
文創不捱餓 // 娛樂小老闆・2016-08-08

至愛新聽力是澳門暫時唯一最具代表性的音樂頒奬典禮,有點像香港的十大勁歌金曲頒奬典禮,老實說在澳門這個小城獲得到音樂奬項並不代表你會大紅大紫,所以與其說大家是競爭奬項,其實這個頒奬禮更像一個年度樂壇派對,無論是歌手、音樂人或娛樂公司,都希望藉此機會讓更多人認識本地的流行音樂。 頒奬典禮,獲奬歌手必然會在台上感謝一大堆人,即使被電視台狂催也堅持要把感謝的人在台上說出來,多謝家人、多謝音樂人、多謝所屬公司等,我不是歌手,我無緣站上頒奬台,但今年我也有很多人希望多謝,而且在我自己的專欄地盤,我可以有無限時間講我的謝詞。 作為PRIDE的負責人,我首先要感謝兩位公司歌手 大頭及欣蒨 ,謝謝你們的信任,在我而言,經理人公司和歌手從來不是上級和下屬的關係,歌手願意將自己的青春、心血和作品貢獻給一家公司,同樣地一家公司願意投入資源 在歌手身上,尤其在澳門這個看似娛樂是無利可圖的地方,完全是基於大家互相信任,互相認同,希望結合雙方力量而為本地樂壇貢獻一分力量,我稱之為信念一 致,有沒有奬項從來不是PRIDE以及我本人首要考慮的條件,我們PRIDE由始至終都希望盡最大努力,在有限的條件下給予自家歌手最好的資源,多謝兩位 讓我們知道努力的價值,多謝兩位的付出讓我們更清楚知道自己的不足,我們必定在日後的路途上繼續努力,期望未來我們會攜手更進一步。 另外我要代表PRIDE上下同事以及歌手向一班幕後音樂人致謝,多謝你們給予機會兩位歌手演繹你們的作品,通過你們的音樂歌手才有機會被更多人認識。最後,我特別要感謝澳門電視台,多謝你們提供了一個這麼好的平台,讓一代又一代的歌手通過這平台成長,讓一家又一家的娛樂公司被更多澳門人認識,我打從心底裡感謝澳門電視台。

The Romeo and Juliet effect
玄學星相
熊神進・2016-08-02

When I first met Miss M, she was just an 18yearold girl with upward eyebrow tails. According to face reading, she was an independent and strongminded girl who would do whatever she wanted. Her parents once did not allow her to leave Macau for studying at a university in Taiwan, but she was still sent to study aboard by Macau government because of her outstanding performance. In the third year of university, she fell in love with an exchange student from Singapore. That boy has a complicated family, his mother was sentenced to 24 years in prison, and his older brother was a local ringleader. Of course Miss Mrsquo;s parents were opposed to their relationship, but the two lovers determined to break through all difficulties to carry on their faithful love till the end. The more opposition from her parents, the closer they became. When two people in love encounter difficulties and have to break up, it induces quot;the Romeo and Juliet effectrdquo;. Because they were unable to change her parentsrsquo; mind, they used ldquo;living togetherrdquo; to prove their true love. They thought that was love Lovers often have a mistaken view about love, they consider the strength of defeating outside difficulties especially parentsrsquo; opposition as the strength of love, and transform the sense of achievement of defeating parentsrsquo; opposition into the feeling of love. This year Miss M came to visit me and told me that she had already divorced. Certainly, there are many reasons for divorce, but I would like to remind you that, the impulsion of love triggered by living together to resist parentsrsquo; opposition usually cannot bear the test of setback. Once two lovers come across sad setback, there will be cracks in their love.

【2020動漫推薦】十五套必追春季新番動漫推薦
娛樂殿堂
Cheers!・2020-04-03

來到4月,又有新動漫要追啦!留在家中看動漫相信是不少澳門動漫迷其中一項消閒活動。今季動漫一如既往,有好多部動漫很值得大家去追看。有些是充滿童年回憶的,有些是和大家闊別多年後再新出的,甚至有些是來到最終章的。事不宜遲,馬上為大家介紹由2020年4月開始,有那些留在家中必追看的日本春季動漫啦! 轉生成女性向遊戲只有毀滅END的壞人大小姐 《異世界》系列的動漫通常都是因為主人公於前世死掉,因而轉生成好人及擁有逆天能力數值的主角,但今次這套《轉生》的女主角卻沒有這般「幸運」。因8歲一次在花園裡摔跤而撞破額頭,從而知道了原來自己本來是一位17歲因意外去世的高中生,現在轉生到她前世玩過的女性向戀愛遊戲「Fortune Lover」。最糟糕的是她竟然是遊戲中的大反派,沒有逆天能力數值不在話下,而且結局不是被流放,就是死亡。今次無主角光環的眷顧之下,主人公究竟能否成功扭轉命運呢? 動畫製作:SILVER LINK. 放送開始:2020年4月4日 刀劍神域 Alicization Underworld 大戰篇 最終季 最緊張而刺激的《刀劍神域》篇章終於要來了!上一季講到人界與暗黑界的大戰已經揭開序幕,及後亞絲娜以超級帳號的女神姿態趕來增援,但光之女巫「愛麗絲」仍然被暗黑神貝庫達成功擄走。正在趕去「世界盡頭之祭壇」之際,詩乃及莉法以女神級數的超級帳號成功趕及增援。除了有三大女神助陣之外,發呆了一整季的「桐人」終於都要清醒起來,拔起雙劍,準備為 UnderWorld 的「最終負荷測試」畫上句號。可惜的是,亞絲娜、詩乃及莉法的一眾粉絲擁躉要等到4月尾才有得欣賞。 動畫製作:A1 Pictures 放送開始:2020年4月25日 神之塔 假如你好喜歡《Kill La Kill》、《鬼滅之刃》和《刀劍神域》等與刀劍有關的熱血動漫的話,千萬不要錯過這套全球閱覽數超過45億的《神之塔》。一座神秘莫測且擁有眾多可怕怪物守候的「神之塔」,正等候著位於塔底、想成為神的人前來接受測試。一位只為想到塔上和朋友「蕾哈爾」追逐繁星的男孩「第二十五夜」,接受了最底層管理者的測試,成功打開塔的大門,展開一場要以生命作為賭注的登塔之旅。單看預告片就已經令人熱血沸騰,今年4月一定要追看呀! 動畫製作:Telecom Animation Film 放送開始:2020年4月1日 輝夜姬想讓人告白?天才們的戀愛頭腦戰~ 2019年冬季推出第一季後大獲好評,而2020年春季繼續乘勢推出第二季的《輝夜姬想讓人告白》,繼續貫徹「在戀愛中先告白的人就是輸家」的愛情觀念。在這個雲集各方面天才的精英學生,不斷進行要讓對方先同自己告白的戀愛頭腦戰!夏天過去後,校內成績第一、單靠後天努力和學習的「白銀御行」,與全校成績僅次於白銀御行、屬於全能型天才的「四宮輝夜」的關係又會否能夠更上一層樓呢?一場只能夠智取的戀愛大戰今年4月一觸即發。 動畫製作:A1 Pictures 放送開始:2020年4月11日 魔神英雄傳 七魂的龍神丸 充滿童年回憶的冒險動漫《魔神英雄傳》最新作終於和大家再次見面啦!其中「機會來了,飛雲!」金句更已經成為今時今日網路潮語。小時候看《魔神英雄傳》的大家,到今日已經長大成人。今次救世主「飛雲」再次被召喚到「創界山」,但龍神丸因為被邪惡之力封印而變得支離破碎,所以出現了上下顛倒的海巿蜃樓景象。為了再次拯救創界山,飛雲再次與虎王、火美子及武一郎等伙伴,一齊踏上冒險旅程。能夠再次見到可愛的火美子真是太好啦! 動畫製作:SUNRISE 放送開始:2020年4月10日 遊戲王 SEVENS 著名卡牌遊戲動漫《遊戲王》今年4月會播出第七季,就連決斗盤都要以數目字「7」來設計。今次的主人公「王道遊我」是一名小學五年級學生,因為受不了大人所訂下的決鬥規則,所以自己發明了一套任何人都可以玩得好開心的決鬥規則。某一天遊我從同學提及「決鬥之王」一事,接著在神秘的石碑前現身了一位重要人物,一場以卡牌來決鬥而斷定勝利就此開始。今季《遊戲王》的主人公年紀屬於孩童級別,而且預告片中的「Rush Duel」屬於快速決鬥類型,再加上令人期待的嶄新卡牌怪獸,以及有可能會出現的新召喚方式,相信今季《遊戲王》有另一番新景象。 動畫製作:Bridge 放送開始:2020年4月4日 邪神與廚二病少女 第二季 一次機緣巧合下,喜歡黑魔法與恐佈電影的中二病女大學生「花園百合鈴」召喚了惡魔邪神醬。被召喚出來的邪神醬很想回到魔界,但只有召喚者死亡才能夠回去,所以不斷嘗試暗殺百合鈴,但全部都失敗,皆因百合鈴經常網購各種武器和刑具來對抗,令到邪神醬被迫要和百合鈴一齊展開既嘈吵又有趣的同居生活。第二季更加會有新角色天使和殭屍姐妹登場,相信到時候會變得更嘈吵、更有趣。 動畫製作:NOMAD 放送開始:2020年4月6日 王者天下 第三季 與對上一季已經過了7年,今季《王者之下》將來是原作漫畫最高潮的一戰!故事的背景是設定在戰國七雄爭霸的時代。孤以出身的主人公「信」以成為天下的大將軍為夢想,繼而遇上因政變而被追殺的秦王嬴政,他們一齊在戰國這個亂世中,為各自的目標而奮鬥。最高潮部分將會是由趙國天才軍師「李牧」所組成的「合縱軍」準備反攻秦國,而秦國將會集結全部兵力去迎擊。相信一眾《王者之下》的粉絲會十分期待這套動畫系列。 動畫製作:Pierrot、Studio Signpost 放送開始:2020年4月5日 八男?別鬧了! 從動漫片名中完全不會看得出來是一套《異世界》戰鬥系列的《八男?別鬧了!》,講述一位25歲的平凡上班族,一覺醒來發現自己被轉生成為一位下級貴族排行第8的小男孩「威爾」。無論是繼承家業或領地都無可能的他,意外地從魔法師傅手上得到裝備和財產,就這樣展開在魔法異世界穩步向上的生活。十分期待異世界戰鬥動漫系列的您,《八男?別鬧了!》相信會值得付出時間去追看。 動畫製作:SHINEI 動畫、SynergySP 放送開始:2020年4月2日 果然我的青春戀愛喜劇搞錯了。完 相當受注目的《果然我的青春戀愛喜劇搞錯了》終於迎來了最終章!沒有朋友和女朋友的單身狗「比企谷八幡」生性彆扭,並設咒享受青春的同班同學,因而被老師帶到去「侍奉社」作思想教育。「侍奉社」內有一位號稱全校第一美少女「雪之下雪乃」,但雪乃和八幡的個性上各有缺陷,令他們始終無辦法擦出愛情火花,從而造成一段充滿錯誤的青春愛情故事。最後八幡能否脫離單身狗行列呢?答案將於今年4月逐步揭曉! 動畫製作:feel. 放送開始:2020年4月9日 小書痴的下剋上:為了成為圖書管理員不擇手段!第二季 在去年秋季動漫中大放異彩的《小書痴的下剋上》今年4月將迎來第二季!身型嬌小的麗乃是一位超級書痴,她被轉生到一個書籍是貴族限定擁有的異世界。她體弱多病且家境貧困,但憑著她的堅毅意志及擁有前世對書籍的知識,一步一步地向著成為圖書管理員的目標前進,是一套很值得追看的勵志動漫! 動畫製作:亞細亞堂 放送開始:2020年4月8日 超異域公主連結 ReDive 改篇自 Cygames 旗下同名 RPG 手機遊戲的《超異域公主連結 ReDive》是以奇幻風格為主。內容講述一位失去記憶的少年「祐樹」在阿斯特雷工大陸醒來。其後他遇上了精靈「可可蘿」、劍士「佩可莉露」及貓耳獸人「凱留」。他們一起成立了公會「美食殿堂」,在命運的引導下展開了冒險旅程。身為《超異域公主連結》玩家的您記得要捧場啦! 動畫製作:Cygames Pictures 放送開始:2020年4月6日 隱瞞之事 為了可愛的女兒不會因為他從事畫下流漫畫的工作而受到欺凌,身為父親的「後藤可久」努力地隱瞞真相。每天穿著西裝出去工作,中途更要換上便服去畫漫畫。一段充滿愛和歡樂的父女之間的故事就此展開,是一套充滿親情而幽默的感人故事,分分鐘可能會加了洋蔥呢! 動畫製作:亞細亞堂 放送開始:2020年4月2日 數碼暴龍大冒險: 另一部會透露真實年齡的《數碼暴龍》系列最新作將會和大家於今年4月見面!和大家闊別了超過廿年的八神太一、石田大和等其他被選中的小朋友,再次進入數碼世界,和阿古獸、加布獸、巴達獸、迪路獸等一眾熟悉的數碼暴龍一齊去進行大冒險。童年回憶返來了! 動畫製作:東映動畫 放送開始:2020年4月5日 LISTENERS 故事講述在一個受到謎之生命體「耳無」威脅的世界中,人類只能夠透過能夠與戰鬥機械裝備進行插入連及操控的能力者「祈手」來對抗。某一日,住在垃圾城巿的少年「艾克沃」,無意間在垃圾山採掘時,找到失去記憶的少女「繆」。她的腰間有插孔,是一名能夠對抗「耳無」的「祈手」。為了尋找祈手「吉米」,艾克沃和繆一齊展開旅程。大家還記得《擅長捉弄人的高木同學》中,為高木同學配音的聲優「高橋李依」嗎?今次她亦將會為《LISTENERS》中的女主角「繆」配音。大家敬請留意啦! 動畫製作:MAPPA 放送開始:2020年4月3日 今年的春季動漫將於2020年4月份開始,當中有多部高質素的動漫,以及充滿童年回憶的經典動漫,讓大家可以留在家中觀看精彩的動漫。上網除了看動漫之外,還可以在家工作、買外賣、買餸、親子學習等。想知道更多留在家中的好處,可查看以下文章。 延伸閱讀:留在家中好方便!教您如何親子學習在家工作消閒外賣買餸攻略 上季日本新番動漫推薦:【2020動漫推薦】八套必追冬季新番動漫推薦 下季日本新番動漫推薦:【2020動漫推薦】十套必追夏季新番動漫推薦 2020年或以後的日本新番動漫推廌:日本新番動漫推薦,每年每季必追動漫列表 作者:IronMan

Spring Boot 05 - 為 http json api 加入登入要求
科技新知
MacauYeah・2024-07-02

本節,我們將為之前的http服務,加入認證機制,只有在資料庫現存的用戶可以登入及訪問我們的json api。 下戴模版 慣例,我們用Spring Initializr Maven 下載模版,Dependency主要選擇 Spring Web Spring Boot DevTools Spring Security Controller 跟上節一樣,我們起一個Controller,為簡化測試,我們只做http GET api。 由於本blog對於Source Code的顯示不太友好,有需要看source code的,請到Github查看 srcmainjavaiogithubmacauyeahspringboottutorialspringbootwebapidatacontrollerHomeController.java import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMappingquot;apiquot; public class HomeController @GetMappingquot;someRecorduuidquot; public Map readSomeRecord@PathVariable String uuid return Map.ofquot;retquot;, quot;your uuidquot; uuid; 準備我們的test case,但這次我們預期它應該要出現登入失敗的結果。 srctestjavaiogithubmacauyeahspringboottutorialspringbootwebapidatacontrollerHomeControllerTest.java @SpringBootTest @AutoConfigureMockMvc public class HomeControllerTest @Autowired private MockMvc mockMvc; @Test void testNoLogin throws Exception RequestBuilder requestBuilder = MockMvcRequestBuilders.getquot;apisomeRecord1234quot; .contentTypeMediaType.APPLICATION_JSON; this.mockMvc.performrequestBuilder .andExpectMockMvcResultMatchers.status.is4xxClientError .andExpectMockMvcResultMatchers.jsonPathquot;$.retquot;.doesNotExist .andDoMockMvcResultHandlers.print; 在我們執行上述的測試,test case 成功過了。我們的基本設定跟上一節其實沒有多大改動,為何現在http api會回傳狀態 401? 那是因為我們在依賴中加了,Spring Security,它配合了Spring Web,就會自動為所有api加入權限檢測。我們的測試中,沒有任何用戶登入,當然會出現 http 401。為了讓我們可以好好管理誰可以使用api,我們就來設定一定Security。 我們加一個WebSecurityConfig.java,暫時指定所有的訪問路徑都必需有USER權限,並且用 http basic的方式登入。 srcmainjavaiogithubmacauyeahspringboottutorialspringbootwebapidataconfigWebSecurityConfig.java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity public class WebSecurityConfig @Bean SecurityFilterChain securityFilterChainHttpSecurity http throws Exception http.authorizeHttpRequestsauthorizeHttpRequests gt; authorizeHttpRequests.requestMatchersquot;quot;.hasRolequot;USERquot;; 所有的訪問路徑都必需有USER權限 ; http.httpBasicCustomizer.withDefaults; 使用http basic作為登入認證的方式 return http.build; 上述例子,只是擋了沒有權限的人,我們還需要讓有登入身份的用戶可以成得取限User權限。 我們繼續修改,WebSecurityConfig,加入只在記憶體有效的InMemoryUser import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.provisioning.InMemoryUserDetailsManager; public class WebSecurityConfig .. @Bean public PasswordEncoder passwordEncoder return new BCryptPasswordEncoder; 我們的密碼不應該明文儲,比較保險,我們使用BCrypt演算法,為密碼做單向加密。 @Bean public UserDetailsService userDetailsService UserDetails user = User.withUsernamequot;adminquot; .passwordpasswordEncoder.encodequot;passquot; .rolesquot;USERquot;.build; 我們在記憶中體,加入一個測試用的User,它的名字為admin,密碼為pass,權限為User return new InMemoryUserDetailsManageruser; 然後加入新的測試,直接模擬Role。結果是通過的。 srctestjavaiogithubmacauyeahspringboottutorialspringbootwebapidatacontrollerHomeControllerTest.java @Test void testLoginWithRoles throws Exception RequestBuilder requestBuilder = MockMvcRequestBuilders.getquot;apisomeRecord1234quot; .contentTypeMediaType.APPLICATION_JSON.with SecurityMockMvcRequestPostProcessors.userquot;someonequot; .rolesquot;USERquot;, quot;ADMINquot;; 沒有使用密碼,只使用Role this.mockMvc.performrequestBuilder .andExpectMockMvcResultMatchers.status.is2xxSuccessful .andExpectMockMvcResultMatchers.jsonPathquot;$.retquot;.valuequot;your uuid1234quot; .andDoMockMvcResultHandlers.print; 再來一個測試,改用密碼登入,分別輸入錯的和正確的密碼。 @Test void testLoginWithWrongPasswordAndNoRole throws Exception RequestBuilder requestBuilder = MockMvcRequestBuilders.getquot;apisomeRecord1234quot; .headerquot;Authorizationquot;, quot;Basic randompassquot; 輸入錯的密碼,應該回傳http 401 Unauthorized .contentTypeMediaType.APPLICATION_JSON; this.mockMvc.performrequestBuilder .andExpectMockMvcResultMatchers.status.is4xxClientError .andDoMockMvcResultHandlers.print; @Test void testLoginWithPassword throws Exception RequestBuilder requestBuilder = MockMvcRequestBuilders.getquot;apisomeRecord1234quot; .headerquot;Authorizationquot;, quot;Basic YWRtaW46cGFzcw==quot; http basic 就是把 adminpass 轉成base64 .contentTypeMediaType.APPLICATION_JSON; this.mockMvc.performrequestBuilder .andExpectMockMvcResultMatchers.status.is2xxSuccessful .andExpectMockMvcResultMatchers.jsonPathquot;$.retquot;.valuequot;your uuid1234quot; .andDoMockMvcResultHandlers.print; 最後,當然是正確的密碼才能通過。若果大家還是半信半疑,我們可以跑起真的正服務(IDE RUN或mvn springbootrun),然後用curl去試。 curl httplocalhost8080apisomeRecord1234 failed with 401 curl u quot;adminpassquot; httplocalhost8080apisomeRecord1234 successed 使用SQL Database讀取用戶登入資訊 一般而言,我們不可能把所有用戶登資訊打在InMemoryUser中,通常背後有一個資料庫儲存所有的用戶資訊,我們在登入時,讀取它來做對比檢證。 為此,我們在maven中,加入 Spring Data JPA h2 database (或任何你的資料庫,如mysql 、 sql server) 最後一步,我們把InMemoryUser去掉,改為從資料庫讀取。因為原始碼太多,就不全部貼上。最主要的是WebSecurityConfig.java要關掉之前的UserDetailsService,改為提供一個UserServiceImpl類,它會實現UserDetailsService的功能。 @Configuration @EnableWebSecurity public class WebSecurityConfig 把原來的Bean先變成註解,其他不變 @Bean public UserDetailsService userDetailsService UserDetails user = User.withUsernamequot;adminquot; .passwordpasswordEncoder.encodequot;passquot; .rolesquot;USERquot;.build; return new InMemoryUserDetailsManageruser; springboottutorialspringbootwebapidatasrcmainjavaiogithubmacauyeahspringboottutorialspringbootwebapidataconfigUserServiceImpl.java other import import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; @Service public class UserServiceImpl implements UserDetailsService @Autowired PasswordEncoder passwordEncoder; @Autowired UserRepo userRepo; @Override public UserDetails loadUserByUsernameString username throws UsernameNotFoundException 因為我們資料庫沒有資料,為了方便測試密碼的加密,我們在java code上直接插入一筆資料。 UserEntity defaultUser = new UserEntity; defaultUser.setUsernamequot;adminquot;; defaultUser.setPasswordpasswordEncoder.encodequot;passquot;; defaultUser.setRolequot;USERquot;; defaultUser.setUuidUUID.randomUUID.toString; userRepo.savedefaultUser; 上述為測試用插入資料,不應該出現在正式使用環境中。 UserEntity user = userRepo.findOneByUsernameusername .orElseThrow gt; new UsernameNotFoundExceptionusername quot; not foundquot;; 找找資料庫有沒有正在登入的該名使用者username List authorities = List.ofnew SimpleGrantedAuthorityquot;ROLE_quot; user.getRole; LOG.debugquot;got user uuid, username, role from databasequot;, user.getUuid, username, user.getRole; 如果前面的 findOneByUsername 有結果回傳,我們就給它一個ROLE_XXX的權限。 return new Userusername, user.getPassword, authorities; 這裏從沒有檢查過密碼是否有匹配,全部交給Spring Security去做 springboottutorialspringbootwebapidatasrcmainjavaiogithubmacauyeahspringboottutorialspringbootwebapidataentityUserEntity.java springboottutorialspringbootwebapidatasrcmainjavaiogithubmacauyeahspringboottutorialspringbootwebapidatarepoUserRepo.java 上述段落中,筆者省略了UserEntity和UserRepo,它們只是一般的springdatajpa概念,有需要可以經文末的連結查看完全原始碼。最需要注意的,是UserEntity的password欄位,在資料庫中是以加密的方式儲存。我們在配匹登入者與資料庫記錄時,也沒有自行檢驗密碼的需要。我們只是在加密過的密碼回傳給Spring Security,Spring框架會自行把登入者輸入的密碼與加密了的密碼作比較。