158 | | |
| 158 | Најавата на системот е имплементирана директно на серверската апликација односно на адресата http://localhost:8080/login, каде што се прикажува страницата од сликата. За реализација на ова сценарио користиме Spring Security каде синџирот филтри е соодветно конфигуриран да одговори на нашите потреби, односно да ги филтрира барањата во зависност од тоа дали корисницте се автентицирани, имаат одредена привилегија или пристапуваат до нивен ресурс. |
| 159 | {{{#!java |
| 160 | @Bean |
| 161 | public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { |
| 162 | http |
| 163 | .csrf().disable() |
| 164 | .authorizeHttpRequests((authz) -> { |
| 165 | try { |
| 166 | authz |
| 167 | .requestMatchers(new AntPathRequestMatcher("/{userId}/hasBusiness")).access(userSecurity) |
| 168 | .requestMatchers(new AntPathRequestMatcher("/business/*/unapproved")).authenticated() |
| 169 | .requestMatchers(new AntPathRequestMatcher("/register")).permitAll() |
| 170 | .requestMatchers(new AntPathRequestMatcher("/hotel/search")).permitAll() |
| 171 | .requestMatchers(new AntPathRequestMatcher("/transport/search")).permitAll() |
| 172 | .requestMatchers(new AntPathRequestMatcher("/restaurant/search")).permitAll() |
| 173 | .requestMatchers(new AntPathRequestMatcher("/upload")).permitAll() |
| 174 | .requestMatchers(new AntPathRequestMatcher("/business/approve/*")).hasAnyAuthority("SUPERADMIN") |
| 175 | .requestMatchers(new AntPathRequestMatcher("/users/unlock/*")).hasAnyAuthority("SUPERADMIN") |
| 176 | .requestMatchers(new AntPathRequestMatcher("/users/approve/*")).hasAnyAuthority("SUPERADMIN") |
| 177 | .requestMatchers(new AntPathRequestMatcher("/business/unapproved")).hasAnyAuthority("SUPERADMIN") |
| 178 | .requestMatchers(new AntPathRequestMatcher("/business/add/*")).authenticated() |
| 179 | .requestMatchers(new AntPathRequestMatcher("/*/user/{userId}")).access(userSecurity) |
| 180 | .anyRequest().authenticated() |
| 181 | .and() |
| 182 | .formLogin() |
| 183 | .loginPage("/login") |
| 184 | .successHandler(oAuth2SuccessHandler) |
| 185 | .permitAll() |
| 186 | .permitAll() |
| 187 | .and() |
| 188 | .sessionManagement() |
| 189 | .sessionCreationPolicy(SessionCreationPolicy.ALWAYS) |
| 190 | .and() |
| 191 | .logout().logoutSuccessHandler((new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK))) |
| 192 | .permitAll(); |
| 193 | |
| 194 | } catch (Exception e) { |
| 195 | throw new RuntimeException(e); |
| 196 | } |
| 197 | } |
| 198 | ).httpBasic(); |
| 199 | return http.build(); |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | }}} |
| 204 | За да се провери идентитетот на корисникот, односно дали тој е сопственик на ресурсите кои се обидува да ги пристапи е имплементирана следната компонента. |
| 205 | {{{#!java |
| 206 | @Component |
| 207 | public class UserSecurity implements AuthorizationManager<RequestAuthorizationContext> { |
| 208 | |
| 209 | @Override |
| 210 | public AuthorizationDecision check(Supplier authenticationSupplier, RequestAuthorizationContext ctx) { |
| 211 | Long userId = Long.parseLong(ctx.getVariables().get("userId")); |
| 212 | Authentication authentication = (Authentication) authenticationSupplier.get(); |
| 213 | return new AuthorizationDecision(hasUserId(authentication, userId)); |
| 214 | } |
| 215 | |
| 216 | public boolean hasUserId(Authentication authentication, Long userId) { |
| 217 | Long id; |
| 218 | User user = (User) authentication.getPrincipal(); |
| 219 | id = user.getUserID(); |
| 220 | return userId == id; |
| 221 | } |
| 222 | |
| 223 | } |
| 224 | }}} |
| 225 | По успешната најава, серверот го пренасочува корисникот до клиентската апликација односно http://localhost:8080/login-callback по што, преку Hook-от useLogin се испраќа барање за добивање на податоците и нивно зачувување во AuthContext-от, од каде можат сите компоненти да ги пристапат. |
| 226 | {{{#!javascript |
| 227 | |
| 228 | const useLogin = () => { |
| 229 | const [loading, setLoading] = useState(false); |
| 230 | const [error, setError] = useState(null); |
| 231 | const Auth = useAuth(); |
| 232 | |
| 233 | const handleLoginCallback = async () => { |
| 234 | setLoading(true); |
| 235 | |
| 236 | try { |
| 237 | const response = await axios.get("http://localhost:8080/principal"); |
| 238 | const { id, role, username } = response.data; |
| 239 | |
| 240 | Auth.userLogin({userId: id, username: username, role: role}) |
| 241 | |
| 242 | } catch (err) { |
| 243 | setError(err.message); |
| 244 | } finally { |
| 245 | setLoading(false); |
| 246 | } |
| 247 | }; |
| 248 | |
| 249 | return { |
| 250 | loading, |
| 251 | error, |
| 252 | handleLoginCallback, |
| 253 | }; |
| 254 | }; |
| 255 | |
| 256 | export default useLogin; |
| 257 | }}} |
| 258 | \\ |
| 259 | {{{#!javascript |
| 260 | const AuthContext = createContext() |
| 261 | |
| 262 | const AuthProvider = ({ children }) => { |
| 263 | const [user, setUser] = useState(null) |
| 264 | |
| 265 | useEffect(() => { |
| 266 | const storedUser = JSON.parse(localStorage.getItem('user')) |
| 267 | setUser(storedUser) |
| 268 | }, []) |
| 269 | |
| 270 | const getUser = () => { |
| 271 | return JSON.parse(localStorage.getItem('user')) |
| 272 | } |
| 273 | |
| 274 | const userIsAuthenticated = () => { |
| 275 | return localStorage.getItem('user') !== null |
| 276 | } |
| 277 | |
| 278 | const userLogin = user => { |
| 279 | localStorage.setItem('user', JSON.stringify(user)) |
| 280 | setUser(user) |
| 281 | } |
| 282 | |
| 283 | const userLogout = () => { |
| 284 | localStorage.removeItem('user') |
| 285 | setUser(null) |
| 286 | } |
| 287 | |
| 288 | const contextValue = { |
| 289 | user, |
| 290 | getUser, |
| 291 | userIsAuthenticated, |
| 292 | userLogin, |
| 293 | userLogout, |
| 294 | } |
| 295 | |
| 296 | return ( |
| 297 | <AuthContext.Provider value={contextValue}> |
| 298 | {children} |
| 299 | </AuthContext.Provider> |
| 300 | ) |
| 301 | } |
| 302 | |
| 303 | export default AuthContext |
| 304 | |
| 305 | export function useAuth() { |
| 306 | return useContext(AuthContext) |
| 307 | } |
| 308 | |
| 309 | export { AuthProvider } |
| 310 | }}} |
| 311 | За да се заштитат рутите за кои е потребна автентикација, имплементирана е wrapper-компонента којашто проверува дали корисникот е најавен и во спротивно го пренасочува кон страницата за најава. |
| 312 | {{{#!javascript |
| 313 | function PrivateRoute({ children }) { |
| 314 | const { userIsAuthenticated } = useAuth() |
| 315 | if(userIsAuthenticated()) |
| 316 | { |
| 317 | return children; |
| 318 | } |
| 319 | else |
| 320 | { |
| 321 | window.location.href = "http://localhost:8080/login"; |
| 322 | } |
| 323 | } |
| 324 | |
| 325 | export default PrivateRoute |
| 326 | }}} |