티스토리 뷰

Dev/Spring

Creating a Custom Spring Security Solutiom

마이스토리 2016. 6. 10. 14:18

> Creating a Custom Spring Security Solutiom

> Components for a Custom Spring Security Solutoion

  • Spring Security Dependency (Maven or Gradle)
  • Entiry Model
  • Data Repositoyr
  • Business Service
  • User Details Service
  • Authentication Provider
  • Security Configuration

> Spring Security Dependency

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

> Entiry Model

@Entity
public class Account {
@Id
@GeneratedValue
private Long id;
@NotNull
private String username;
@NotNull
private String password;
@NotNull
private boolean enabled = true;
@NotNull
private boolean credentialsexpired = false;
@NotNull
private boolean expired = false;
@NotNull
private boolean locked = false;
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable(name = "AccountRole",  
joinColumns = @JoinColumn(name = "accountId", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "roleId", referencedColumnName = "id") )
private Set<Role> roles;
public Account(){
}

@Entity

public class Role {

@Id

private Long id;

@NotNull

private String code;

@NotNull

private String label;

public Role(){

}


CREATE TABLE Account (

  id BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1) NOT NULL,

  referenceId VARCHAR(255) NOT NULL,

  username VARCHAR(100) NOT NULL,

  password VARCHAR(200) NOT NULL,

  enabled BOOLEAN DEFAULT true NOT NULL,

  credentialsexpired BOOLEAN DEFAULT false NOT NULL,

  expired BOOLEAN DEFAULT false NOT NULL,

  locked BOOLEAN DEFAULT false NOT NULL,

  version INT NOT NULL,

  createdBy VARCHAR(100) NOT NULL,

  createdAt DATETIME NOT NULL,

  updatedBy VARCHAR(100) DEFAULT NULL,

  updatedAt DATETIME DEFAULT NULL,

  PRIMARY KEY (id),

  CONSTRAINT UQ_Account_ReferenceId UNIQUE (referenceId),

  CONSTRAINT UQ_Account_Username UNIQUE (username)

);


CREATE TABLE Role (

  id BIGINT NOT NULL,

  code VARCHAR(50) NOT NULL,

  label VARCHAR(100) NOT NULL,

  ordinal INT NOT NULL,

  effectiveAt DATETIME NOT NULL,

  expiresAt DATETIME DEFAULT NULL,

  createdAt DATETIME NOT NULL,

  PRIMARY KEY (id),

  CONSTRAINT UQ_Role_Code UNIQUE (code)

);


CREATE TABLE AccountRole (

  accountId BIGINT NOT NULL,

  roleId BIGINT NOT NULL,

  PRIMARY KEY (accountId, roleId),

  CONSTRAINT FK_AccountRole_AccountId FOREIGN KEY (accountId) REFERENCES Account (id),

  CONSTRAINT FK_AccountRole_RoleId FOREIGN KEY (roleId) REFERENCES Role (id)

);


-- password is 'password'

INSERT INTO Account (referenceId, username, password, enabled, credentialsexpired, expired, locked, version, createdBy, createdAt, updatedBy, updatedAt) VALUES ('a07bd221-3ecd-4893-a0f0-78d7c0fbf94e', 'user', '$2a$10$9/44Rne7kQqPXa0cY6NfG.3XzScMrCxFYjapoLq/wFmHz7EC9praK', true, false, false, false, 0, 'user', NOW(), NULL, NULL);

-- password is 'operations'

INSERT INTO Account (referenceId, username, password, enabled, credentialsexpired, expired, locked, version, createdBy, createdAt, updatedBy, updatedAt) VALUES ('7bd137c8-ab64-4a45-bf2d-d9bae3574622', 'operations', '$2a$10$CoMVfutnv1qZ.fNlHY1Na.rteiJhsDF0jB1o.76qXcfdWN6As27Zm', true, false, false, false, 0, 'user', NOW(), NULL, NULL);


INSERT INTO Role (id, code, label, ordinal, effectiveAt, expiresAt, createdAt) VALUES (1, 'ROLE_USER', 'User', 0, '2015-01-01 00:00:00', NULL, NOW());

INSERT INTO Role (id, code, label, ordinal, effectiveAt, expiresAt, createdAt) VALUES (2, 'ROLE_ADMIN', 'Admin', 1, '2015-01-01 00:00:00', NULL, NOW());

INSERT INTO Role (id, code, label, ordinal, effectiveAt, expiresAt, createdAt) VALUES (3, 'ROLE_SYSADMIN', 'System Admin', 2, '2015-01-01 00:00:00', NULL, NOW());


INSERT INTO AccountRole (accountId, roleId) SELECT a.id, r.id FROM Account a, Role r WHERE a.username = 'user' and r.id = 1;

INSERT INTO AccountRole (accountId, roleId) SELECT a.id, r.id FROM Account a, Role r WHERE a.username = 'operations' and r.id = 3;



> Data Repository

@Repository

public interface AccountRepository extends JpaRepository<Account, Long> {


Account findByUsername(String username);

}


> Business Service

@Service

public class AccountService {

@Autowired

AccountRepository accountRepository;

public Account findByUsername(String username){

return accountRepository.findByUsername(username);

}

}


> User Detail Service

@Service
public class AccountUserDetailsService implements UserDetailsService {

@Autowired
private AccountService accountService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Account account = accountService.findByUsername(username);
if (account == null){
return null;
}
Collection<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
for(Role role : account.getRoles()){
grantedAuthorities.add(new SimpleGrantedAuthority(role.getCode()));
}
User userDetails = new User(account.getUsername(), account.getPassword(), account.isEnabled(), 
!account.isExpired(), !account.isCredentialsexpired(),
!account.isLocked(), grantedAuthorities);
return userDetails;
}

}


> Common Application Layers

  • Entity Model
  • Data Repositories (DAO)
  • Business Services
  • Facade (or Orchestration Layer)
  • Web Controller
  • Security

> Benefits of Technical Planning and Design

  • ↑Resue
  • ↑Cohension
  • ↑Maintainablility
  • ↑Flexibility = Agility
  • ↓Cost

> Authentication Provider

@Component
public class AccountAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
@Autowired
private AccountUserDetailsService userDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken token)
throws AuthenticationException {
if (token.getCredentials() == null || userDetails.getPassword() == null){
throw new BadCredentialsException("Credentials may not be null.");
}
if (!passwordEncoder.matches((String)token.getCredentials(), userDetails.getPassword())){
throw new BadCredentialsException("Invalid credentials");
}
}

@Override
protected UserDetails retrieveUser(String username,
UsernamePasswordAuthenticationToken token)
throws AuthenticationException {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
return userDetails;
}

}

> Spring Configuration

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
@Autowired
private AccountAuthenticationProvider accountAuthenticationProvider;
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth){
auth.authenticationProvider(accountAuthenticationProvider);
}
@Configuration
@Order(1)
public static class ApiWebSecurityConfigureAdapter extends WebSecurityConfigurerAdapter{

@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
 .antMatcher("/api/**")
   .authorizeRequests()
     .anyRequest().hasRole("USER")
  .and()
  .httpBasic()
  .and()
  .sessionManagement()
    .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// @formatter:on
}
}
@Configuration
@Order(2)
public static class ActuatorWebSecurityConfigureAdapter extends WebSecurityConfigurerAdapter{

@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
 .antMatcher("/actuators/**")
   .authorizeRequests()
     .anyRequest().hasRole("SYSADMIN")
  .and()
  .httpBasic()
  .and()
  .sessionManagement()
    .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// @formatter:on
}
}
}


댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28