Примеры к spring security

This commit is contained in:
kataus
2022-05-25 19:48:48 +03:00
parent 27a5fc1157
commit 4b00f83a20
54 changed files with 1369 additions and 2 deletions
+2 -2
View File
@@ -5,13 +5,13 @@
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>spring-framework-22-spring-security-start</artifactId>
<artifactId>spring-framework-24-spring-security-start</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
<version>2.6.7</version>
</parent>
<properties>
@@ -0,0 +1,12 @@
package ru.otus.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class);
}
}
@@ -0,0 +1,12 @@
package ru.otus.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class);
}
}
@@ -0,0 +1,43 @@
package ru.otus.spring.rest;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class PagesController {
@GetMapping("/")
public String indexPage() {
return "index";
}
@GetMapping("/public")
public String publicPage() {
// SecurityContext securityContext = SecurityContextHolder.getContext();
// Authentication authentication = securityContext.getAuthentication();
// System.out.println(authentication.getPrincipal());
return "public";
}
@GetMapping("/authenticated")
public String authenticatedPage() {
SecurityContext securityContext = SecurityContextHolder.getContext();
return "authenticated";
}
@GetMapping("/success")
public String successPage() {
return "success";
}
@GetMapping("/error")
public String errorPage() {
return "error";
}
}
@@ -0,0 +1,43 @@
package ru.otus.spring.security;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
public class AnonimusUD implements UserDetails {
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return null;
}
@Override
public String getUsername() {
return "anonymous";
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
@@ -0,0 +1,51 @@
package ru.otus.spring.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure( WebSecurity web ) {
web.ignoring().antMatchers( "/" );
}
@Override
public void configure( HttpSecurity http ) throws Exception {
http.csrf().disable()
// По умолчанию SecurityContext хранится в сессии
// Это необходимо, чтобы он нигде не хранился
// и данные приходили каждый раз с запросом
// .sessionManagement().sessionCreationPolicy( SessionCreationPolicy.STATELESS )
// .and()
.authorizeRequests().antMatchers( "/public" ).anonymous()
.and()
.authorizeRequests().antMatchers( "/authenticated", "/success" ).authenticated()
.and()
// Включает Form-based аутентификацию
.formLogin()
;
}
@SuppressWarnings("deprecation")
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
@Autowired
public void configure( AuthenticationManagerBuilder auth ) throws Exception {
auth.inMemoryAuthentication()
.withUser( "admin" ).password( "password" ).roles( "ADMIN" );
}
}
@@ -0,0 +1,17 @@
package ru.otus.spring.security.filter;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
public class MyOwnFilter extends GenericFilterBean {
@Override
public void doFilter( ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain ) throws IOException, ServletException {
servletRequest.getParameterMap().put( "SpecialValue", new String[]{ "My dirty secret" } );
filterChain.doFilter( servletRequest, servletResponse );
}
}
@@ -0,0 +1,4 @@
logging:
level:
root: error
org.springframework: error
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Только для авторизованных</title>
</head>
<body>
Только для авторизованных
</body>
</html>
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Упс...</title>
</head>
<body>
Что-то пошло не так. Печалька
</body>
</html>
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Главная страница</title>
</head>
<body>
<a th:href="@{public}">/public</a>
<br>
<a th:href="@{authenticated}">/authenticated</a>
</body>
</html>
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Доступен всем</title>
</head>
<body>
Доступен всем
</body>
</html>
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Вы успешно вошли !</title>
</head>
<body>
Вы успешно вошли !
</body>
</html>
@@ -0,0 +1,32 @@
package ru.otus.spring.rest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class)
@WebMvcTest(PagesController.class)
public class PagesControllerTest {
@Autowired
private MockMvc mockMvc;
@WithMockUser(
username = "admin",
authorities = {"ROLE_ADMIN"}
)
@Test
public void testAuthenticatedOnAdmin() throws Exception {
mockMvc.perform(get("/authenticated"))
.andExpect(status().isOk());
}
}
+47
View File
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>spring-framework-24-security-authorization</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,17 @@
package ru.otus.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
@SpringBootApplication
public class SpringSecurity24Sample {
public static void main( String[] args ) {
SpringApplication.run( SpringSecurity24Sample.class );
}
}
@@ -0,0 +1,57 @@
package ru.otus.spring.rest;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import ru.otus.spring.service.MyService;
@Controller
public class PagesController {
private final MyService myService;
public PagesController(MyService myService) {
this.myService = myService;
}
@GetMapping("/")
public String indexPage() {
return "index";
}
@GetMapping("/public")
public String publicPage( ) {
return "public";
}
@GetMapping("/user")
public String userPage() {
// myService.onlyUser();
return "user";
}
@GetMapping("/admin")
// @Secured( "ADMIN" )
public String adminPage( ) {
myService.onlyUser();
//myService.onlyAdmin();
return "admin";
}
@GetMapping("/authenticated")
public String authenticatedPage() {
UserDetails userDetails = (UserDetails) SecurityContextHolder
.getContext().getAuthentication().getPrincipal();
System.out.println(userDetails.getUsername());
return "authenticated";
}
@GetMapping("/success")
public String successPage() {
return "success";
}
}
@@ -0,0 +1,16 @@
package ru.otus.spring.rest;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import java.util.Optional;
@ControllerAdvice
public class SecurityControllerAdvice {
@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<String> accessError(){
return ResponseEntity.of( Optional.of( "Неудачник" ));
}
}
@@ -0,0 +1,76 @@
package ru.otus.spring.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import java.util.Collection;
@EnableWebSecurity
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
//.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
//.and()
.authorizeRequests().antMatchers("/public").permitAll()
.and()
.authorizeRequests().antMatchers("/authenticated", "/success").authenticated()
.and()
.authorizeRequests().antMatchers("/user").hasAnyRole( "ADMIN", "USER" )
.and()
.authorizeRequests().antMatchers("/admin").hasRole( "ADMIN" )
.and()
.formLogin()
.and()
.logout().logoutUrl("/logout");
}
@Bean
public PasswordEncoder passwordEncoder() {
return new PasswordEncoder() {
@Override
public String encode(CharSequence charSequence) {
return charSequence.toString();
}
@Override
public boolean matches(CharSequence charSequence, String s) {
return charSequence.toString().equals(s);
}
};
}
@Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin").password("password").roles("ADMIN")
.and()
.withUser("user").password("password").roles("USER")
.and()
.withUser("manager").password("manager").roles("MANAGER", "USER");
}
}
@@ -0,0 +1,16 @@
package ru.otus.spring.service;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
@Service
public class MyService {
@PreAuthorize("hasRole('ROLE_USER') && {new java.util.Random().nextInt()%2 == 0}")
public String onlyUser() {
return "My love";
}
@Secured( "ADMIN" )
public void onlyAdmin() {}
}
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
</head>
<body>
Страница с доступом только админу
</body>
</html>
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
</head>
<body>
Только для аторизованных
</body>
</html>
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
</head>
<body>
Вам доступ запрещён!
</body>
</html>
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
</head>
<body>
<a th:href="@{public}">/public</a>
<br>
<a th:href="@{authenticated}">/authenticated</a>
<br>
<a th:href="@{user}">/user</a>
<br>
<a th:href="@{admin}">/admin</a>
</body>
</html>
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
</head>
<body>
Доступ к MANAGER
</body>
</html>
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
</head>
<body>
Доступен всем
</body>
</html>
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
</head>
<body>
Вы успешно вошли !
</body>
</html>
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
</head>
<body>
Доступ к USER
</body>
</html>
+82
View File
@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>spring-framework-25-acl</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
</parent>
<properties>
<ehcache-core.version>2.6.11</ehcache-core.version>
<swagger.version>2.8.0</swagger.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Spring Security ACL и зависимости-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-acl</artifactId>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>${ehcache-core.version}</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,14 @@
package ru.otus.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class);
}
}
@@ -0,0 +1,23 @@
package ru.otus.spring.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import ru.otus.spring.rest.NoticeMessageController;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket productApi() {
return new Docket(DocumentationType.SWAGGER_2)
.useDefaultResponseMessages(false)
.select()
.apis(RequestHandlerSelectors.basePackage(NoticeMessageController.class.getPackage().getName()))
.build();
}
}
@@ -0,0 +1,33 @@
package ru.otus.spring.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "system_message")
public class NoticeMessage {
@Id
@Column
private Integer id;
@Column
private String content;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
@@ -0,0 +1,4 @@
package ru.otus.spring.model;
public class Pack {
}
@@ -0,0 +1,25 @@
package ru.otus.spring.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PreAuthorize;
import ru.otus.spring.model.NoticeMessage;
import java.util.List;
import java.util.Optional;
public interface NoticeMessageRepository extends JpaRepository<NoticeMessage, Integer> {
@PostFilter("hasPermission(filterObject, 'READ')")
List<NoticeMessage> findAll();
@PostAuthorize("hasPermission(returnObject, 'READ')")
NoticeMessage getById(Integer id);
@SuppressWarnings("unchecked")
@PreAuthorize("hasPermission(#noticeMessage, 'WRITE')")
NoticeMessage save(@Param("noticeMessage") NoticeMessage noticeMessage);
}
@@ -0,0 +1,35 @@
package ru.otus.spring.rest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RestController;
import ru.otus.spring.model.NoticeMessage;
import ru.otus.spring.repository.NoticeMessageRepository;
import java.util.List;
@RestController
public class NoticeMessageController {
private final NoticeMessageRepository repository;
public NoticeMessageController(NoticeMessageRepository repository) {
this.repository = repository;
}
@GetMapping("/message")
public List<NoticeMessage> getAll() {
return repository.findAll();
}
@GetMapping("/message/{id}")
public NoticeMessage getById(@PathVariable("id") Integer id) {
return repository.getById(id);
}
@PutMapping("/message")
public NoticeMessage getById(NoticeMessage message) {
return repository.save(message);
}
}
@@ -0,0 +1,79 @@
package ru.otus.spring.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.ehcache.EhCacheFactoryBean;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.acls.AclPermissionCacheOptimizer;
import org.springframework.security.acls.AclPermissionEvaluator;
import org.springframework.security.acls.domain.*;
import org.springframework.security.acls.jdbc.BasicLookupStrategy;
import org.springframework.security.acls.jdbc.JdbcMutableAclService;
import org.springframework.security.acls.jdbc.LookupStrategy;
import org.springframework.security.acls.model.PermissionGrantingStrategy;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import javax.sql.DataSource;
import java.util.Objects;
@Configuration
public class AclConfig {
@SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
@Autowired
private DataSource dataSource;
@Bean
public EhCacheBasedAclCache aclCache() {
return new EhCacheBasedAclCache(
Objects.requireNonNull(aclEhCacheFactoryBean().getObject()),
permissionGrantingStrategy(),
aclAuthorizationStrategy()
);
}
@Bean
public EhCacheFactoryBean aclEhCacheFactoryBean() {
EhCacheFactoryBean ehCacheFactoryBean = new EhCacheFactoryBean();
ehCacheFactoryBean.setCacheManager(aclCacheManager().getObject());
ehCacheFactoryBean.setCacheName("aclCache");
return ehCacheFactoryBean;
}
@Bean
public EhCacheManagerFactoryBean aclCacheManager() {
return new EhCacheManagerFactoryBean();
}
@Bean
public PermissionGrantingStrategy permissionGrantingStrategy() {
return new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger());
}
@Bean
public AclAuthorizationStrategy aclAuthorizationStrategy() {
return new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_EDITOR"));
}
@Bean
public MethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
AclPermissionEvaluator permissionEvaluator = new AclPermissionEvaluator(aclService());
expressionHandler.setPermissionEvaluator(permissionEvaluator);
expressionHandler.setPermissionCacheOptimizer(new AclPermissionCacheOptimizer(aclService()));
return expressionHandler;
}
@Bean
public LookupStrategy lookupStrategy() {
return new BasicLookupStrategy(dataSource, aclCache(), aclAuthorizationStrategy(), new ConsoleAuditLogger());
}
@Bean
public JdbcMutableAclService aclService() {
return new JdbcMutableAclService(dataSource, lookupStrategy(), aclCache());
}
}
@@ -0,0 +1,22 @@
package ru.otus.spring.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class AclMethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
@SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
@Autowired
MethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler;
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return defaultMethodSecurityExpressionHandler;
}
}
@@ -0,0 +1,60 @@
package ru.otus.spring.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/swagger-ui.html")
.antMatchers("/webjars/springfox-swagger-ui/**")
.antMatchers("/swagger-resources/**")
.antMatchers("/v2/api-docs")
.antMatchers("/h2-console/**");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
//.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
//.and()
.authorizeRequests().antMatchers("/**").permitAll()
.and()
.formLogin()
.and()
.logout().logoutUrl("/logout");
}
@Bean
public PasswordEncoder passwordEncoder() {
return new PasswordEncoder() {
@Override
public String encode(CharSequence charSequence) {
return charSequence.toString();
}
@Override
public boolean matches(CharSequence charSequence, String s) {
return charSequence.toString().equals(s);
}
};
}
@Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin").password("password").roles("EDITOR")
.and()
.withUser("user").password("password").roles("USER")
.and()
.withUser("someone").password("password").roles("SOMEONE");
}
}
@@ -0,0 +1,42 @@
package ru.otus.spring.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.acls.domain.GrantedAuthoritySid;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.domain.PrincipalSid;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.MutableAclService;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import ru.otus.spring.model.NoticeMessage;
import ru.otus.spring.repository.NoticeMessageRepository;
@Service
public class NoticeService {
@Autowired
protected MutableAclService mutableAclService;
@Autowired
private NoticeMessageRepository repository;
public void add( NoticeMessage noticeMessage ) {
repository.save( noticeMessage );
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
final Sid owner = new PrincipalSid( authentication );
ObjectIdentity oid = new ObjectIdentityImpl( noticeMessage.getClass(), noticeMessage.getId() );
final Sid admin = new GrantedAuthoritySid("ROLE_EDITOR");
MutableAcl acl = mutableAclService.createAcl( oid );
acl.setOwner( owner );
acl.insertAce( acl.getEntries().size(), BasePermission.ADMINISTRATION, admin, true );
mutableAclService.updateAcl( acl );
}
}
@@ -0,0 +1,7 @@
spring:
h2:
console:
enabled: true
jpa:
hibernate:
ddl-auto: none
@@ -0,0 +1,28 @@
INSERT INTO system_message(id,content) VALUES
(1,'First Level Message'),
(2,'Second Level Message'),
(3,'Third Level Message');
INSERT INTO acl_sid (id, principal, sid) VALUES
(1, 1, 'admin'),
(2, 1, 'user'),
(3, 0, 'ROLE_EDITOR');
INSERT INTO acl_class (id, class) VALUES
(1, 'ru.otus.spring.model.NoticeMessage');
INSERT INTO acl_object_identity (id, object_id_class, object_id_identity, parent_object, owner_sid, entries_inheriting) VALUES
(1, 1, 1, NULL, 3, 0),
(2, 1, 2, NULL, 3, 0),
(3, 1, 3, NULL, 3, 0);
INSERT INTO acl_entry (id, acl_object_identity, ace_order, sid, mask,
granting, audit_success, audit_failure) VALUES
(1, 1, 1, 1, 1, 1, 1, 1),
(2, 1, 2, 1, 2, 1, 1, 1),
(3, 1, 3, 3, 1, 1, 1, 1),
(4, 2, 1, 2, 1, 1, 1, 1),
(5, 2, 2, 3, 1, 1, 1, 1),
(6, 3, 1, 3, 1, 1, 1, 1),
(7, 3, 2, 3, 2, 1, 1, 1);
@@ -0,0 +1,58 @@
create table IF NOT EXISTS system_message (id integer not null, content varchar(255), primary key (id));
CREATE TABLE IF NOT EXISTS acl_sid (
id bigint(20) NOT NULL AUTO_INCREMENT,
principal tinyint(1) NOT NULL,
sid varchar(100) NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY unique_uk_1 (sid,principal)
);
CREATE TABLE IF NOT EXISTS acl_class (
id bigint(20) NOT NULL AUTO_INCREMENT,
class varchar(255) NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY unique_uk_2 (class)
);
CREATE TABLE IF NOT EXISTS acl_entry (
id bigint(20) NOT NULL AUTO_INCREMENT,
acl_object_identity bigint(20) NOT NULL,
ace_order int(11) NOT NULL,
sid bigint(20) NOT NULL,
mask int(11) NOT NULL,
granting tinyint(1) NOT NULL,
audit_success tinyint(1) NOT NULL,
audit_failure tinyint(1) NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY unique_uk_4 (acl_object_identity,ace_order)
);
CREATE TABLE IF NOT EXISTS acl_object_identity (
id bigint(20) NOT NULL AUTO_INCREMENT,
object_id_class bigint(20) NOT NULL,
object_id_identity bigint(20) NOT NULL,
parent_object bigint(20) DEFAULT NULL,
owner_sid bigint(20) DEFAULT NULL,
entries_inheriting tinyint(1) NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY unique_uk_3 (object_id_class,object_id_identity)
);
ALTER TABLE acl_entry
ADD FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity(id);
ALTER TABLE acl_entry
ADD FOREIGN KEY (sid) REFERENCES acl_sid(id);
--
-- Constraints for table acl_object_identity
--
ALTER TABLE acl_object_identity
ADD FOREIGN KEY (parent_object) REFERENCES acl_object_identity (id);
ALTER TABLE acl_object_identity
ADD FOREIGN KEY (object_id_class) REFERENCES acl_class (id);
ALTER TABLE acl_object_identity
ADD FOREIGN KEY (owner_sid) REFERENCES acl_sid (id);
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
</head>
<body>
Вам доступ запрещён!
</body>
</html>
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
</head>
<body>
<a th:href="@{login}">login</a>
<br>
<a th:href="@{logout}">logout</a>
<br>
<a th:href="@{h2-console}">h2-console</a>
<br>
<a th:href="@{swagger-ui.html}">/swagger</a>
</body>
</html>
+72
View File
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>spring-framework-25-webflux</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.security</groupId>-->
<!-- <artifactId>spring-security-webflux</artifactId>-->
<!-- <version>5.0.0.M5</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestone</id>
<url>http://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
@@ -0,0 +1,22 @@
package ru.otus.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class);
}
@Bean
RouterFunction<ServerResponse> staticResourceRouter() {
return RouterFunctions.resources("/**.html", new ClassPathResource("static/"));
}
}
@@ -0,0 +1,41 @@
package ru.otus.spring.data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
public class Person {
@Id
private String id;
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public Person(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@@ -0,0 +1,11 @@
package ru.otus.spring.data;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Mono;
@Repository
public interface PersonRepository extends ReactiveMongoRepository<Person, String> {
Mono<Person> findByName(String string);
}
@@ -0,0 +1,34 @@
package ru.otus.spring.rest;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import ru.otus.spring.data.Person;
import ru.otus.spring.data.PersonRepository;
@RestController
public class PersonController {
private final PersonRepository personRepository;
public PersonController(PersonRepository personRepository) {
this.personRepository = personRepository;
}
@GetMapping("/person")
public Flux<Person> getAll() {
return personRepository.findAll();
}
@GetMapping("/person/find")
public Mono<Person> find(@RequestParam("name") String name) {
return personRepository.findByName(name)
.cache();
}
@PostMapping("/person")
public Mono<Person> savePerson(@RequestBody Person person) {
return personRepository.save(person);
}
}
@@ -0,0 +1,49 @@
package ru.otus.spring.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain;
@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class SecurityConfiguration {
@Bean
public SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) {
return http
.authorizeExchange()
.pathMatchers(HttpMethod.GET, "/authenticated.html").hasRole("USER")
// anonymous
.anyExchange().permitAll()
.and()
.httpBasic()
.and()
.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
@Bean
public ReactiveUserDetailsService userDetailsService() {
UserDetails user = User
.withUsername("user")
.password("password")
.roles("USER")
.build();
return new MapReactiveUserDetailsService(user);
}
}
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
</head>
<body>
Только для аторизованных
</body>
</html>
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
</head>
<body>
<a href="public.html">/public.html</a>
<br>
<a href="authenticated.html">/authenticated.html</a>
</body>
</html>
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
</head>
<body>
Доступен всем
</body>
</html>