{"id":2267,"date":"2021-07-17T18:13:56","date_gmt":"2021-07-17T09:13:56","guid":{"rendered":"https:\/\/www.skyer9.pe.kr\/wordpress\/?p=2267"},"modified":"2021-07-18T14:43:38","modified_gmt":"2021-07-18T05:43:38","slug":"spring-security-with-jdbcuserdetailsservice","status":"publish","type":"post","link":"https:\/\/www.skyer9.pe.kr\/wordpress\/?p=2267","title":{"rendered":"Spring Security with JDBC(UserDetailsService)"},"content":{"rendered":"<h1>Spring Security with JDBC(UserDetailsService)<\/h1>\n<h2>\uc804\uccb4\uc18c\uc2a4<\/h2>\n<p>\uc804\uccb4\uc18c\uc2a4\ub294 <a href=\"https:\/\/github.com\/skyer9\/JdbcSecurityApp\">\uc5ec\uae30<\/a> \uc5d0 \uacf5\uac1c\ub418\uc5b4 \uc788\uc2b5\ub2c8\ub2e4.<\/p>\n<h2>\ud504\ub85c\uc81d\ud2b8 \uc0dd\uc131<\/h2>\n<p><a href=\"https:\/\/www.skyer9.pe.kr\/wordpress\/?p=2258\">\uc6f9\uc11c\ube44\uc2a4\uc5d0 \ubcf4\uc548 \uc801\uc6a9<\/a> \uc744 \uae30\ucd08\ub85c \uc2dc\uc791\ud569\ub2c8\ub2e4.<\/p>\n<h2>\uc758\uc874\uc131 \ucd94\uac00<\/h2>\n<p>build.gradle \uc5d0 jdbc, jpa, mysql-connector \ub97c \ucd94\uac00\ud574 \uc90d\ub2c8\ub2e4.<\/p>\n<pre><code class=\"language-gradle\">dependencies {\n    implementation &#039;org.springframework.boot:spring-boot-starter-thymeleaf&#039;\n    implementation &#039;org.springframework.boot:spring-boot-starter-web&#039;\n    implementation &#039;org.springframework.boot:spring-boot-starter-security&#039;\n    implementation &#039;org.springframework.boot:spring-boot-starter-data-jdbc&#039;\n    implementation &#039;org.springframework.boot:spring-boot-starter-data-jpa&#039;\n\n    runtimeOnly &#039;mysql:mysql-connector-java&#039;\n\n    compileOnly &#039;org.projectlombok:lombok&#039;\n    developmentOnly &#039;org.springframework.boot:spring-boot-devtools&#039;\n    annotationProcessor &#039;org.projectlombok:lombok&#039;\n    testImplementation &#039;org.springframework.boot:spring-boot-starter-test&#039;\n    implementation &#039;org.springframework.security:spring-security-test&#039;\n}<\/code><\/pre>\n<h2>application.properties \uc124\uc815<\/h2>\n<pre><code class=\"language-properties\"># spring.jpa.hibernate.ddl-auto=update\nspring.datasource.url=jdbc:mysql:\/\/${MYSQL_HOST:localhost}:3306\/db_example\nspring.datasource.username=springuser\nspring.datasource.password=ThePassword\nspring.datasource.driver-class-name =com.mysql.jdbc.Driver\n# spring.jpa.show-sql: true<\/code><\/pre>\n<h2>User \ucd94\uac00<\/h2>\n<p>User.java<\/p>\n<pre><code class=\"language-java\">@Getter\n@Setter\n@Entity\n@Table(name = &quot;tbl_user&quot;, catalog = &quot;db_example&quot;)\npublic class User {\n\n    @Id\n    @GeneratedValue(strategy = GenerationType.AUTO)\n    private Long id;\n\n    @Column(nullable = false, unique = true)\n    private String username;\n\n    @Column(nullable = false)\n    private String password;\n\n    @OneToMany(cascade=CascadeType.ALL,fetch = FetchType.EAGER)\n    @JoinColumn(name=&quot;uid&quot;)\n    private List&lt;UserRole&gt; roles;\n}<\/code><\/pre>\n<p>UserRepository.java<\/p>\n<pre><code class=\"language-java\">public interface UserRepository extends JpaRepository&lt;User, Long&gt; {\n\n    User findByUsername(String username);\n}<\/code><\/pre>\n<p>UserRole.java<\/p>\n<pre><code class=\"language-java\">@Getter\n@Setter\n@Entity\n@Table(name = &quot;tbl_userrole&quot;, catalog = &quot;db_example&quot;)\npublic class UserRole {\n\n    @Id\n    @GeneratedValue(strategy = GenerationType.AUTO)\n    private Long id;\n\n    @Column(nullable = false)\n    private Long uid;\n\n    @Column(nullable = false)\n    private String rolename;\n}<\/code><\/pre>\n<p>UserRoleRepository.java<\/p>\n<pre><code class=\"language-java\">public interface UserRoleRepository extends JpaRepository&lt;UserRole, Long&gt; {\n}<\/code><\/pre>\n<h2>UserDetailsService \ucd94\uac00<\/h2>\n<p>MyUserDetailsService.java<\/p>\n<pre><code class=\"language-java\">@Service\npublic class MyUserDetailsService implements UserDetailsService {\n\n    @Autowired\n    private UserRepository userRepository;\n\n    @Override\n    public UserDetails loadUserByUsername(String username) {\n        User user = userRepository.findByUsername(username);\n        if (user == null) {\n            throw new UsernameNotFoundException(username);\n        }\n        return new MyUserDetails(user);\n    }\n}<\/code><\/pre>\n<p>MyUserDetails.java<\/p>\n<pre><code class=\"language-java\">public class MyUserDetails implements UserDetails {\n    private final User user;\n\n    public MyUserDetails(User user) {\n        this.user = user;\n    }\n\n    @Override\n    public Collection&lt;? extends GrantedAuthority&gt; getAuthorities() {\n        List&lt;GrantedAuthority&gt; roles = new ArrayList&lt;GrantedAuthority&gt;();\n\n        for (UserRole role : this.user.getRoles()) {\n            roles.add(new SimpleGrantedAuthority(role.getRolename()));\n        }\n\n        return roles;\n    }\n\n    @Override\n    public String getPassword() {\n        return user.getPassword();\n    }\n\n    @Override\n    public String getUsername() {\n        return user.getUsername();\n    }\n\n    @Override\n    public boolean isAccountNonExpired() {\n        return true;\n    }\n\n    @Override\n    public boolean isAccountNonLocked() {\n        return true;\n    }\n\n    @Override\n    public boolean isCredentialsNonExpired() {\n        return true;\n    }\n\n    @Override\n    public boolean isEnabled() {\n        return true;\n    }\n}<\/code><\/pre>\n<pre><code class=\"language-sql\">CREATE TABLE `tbl_user` (\n    `id` INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,\n    `username` VARCHAR(255) NOT NULL,\n    `password` VARCHAR(255) NOT NULL\n)\nCOLLATE=&#039;utf8_general_ci&#039;\nENGINE=INNODB;\n\nINSERT INTO tbl_user(username, PASSWORD)\nVALUES(&#039;111&#039;, &#039;{noop}222&#039;);\n\nCREATE TABLE `tbl_userrole` (\n    `id` INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,\n    `uid` INT(11) NOT NULL,\n    `rolename` VARCHAR(255) NOT NULL\n)\nCOLLATE=&#039;utf8_general_ci&#039;\nENGINE=INNODB;\n\nINSERT INTO tbl_userrole(uid, rolename)\nVALUES(1, &#039;ROLE_ADMIN&#039;);\n\nINSERT INTO tbl_userrole(uid, rolename)\nVALUES(1, &#039;ROLE_USER&#039;);<\/code><\/pre>\n<h2>WebSecurityConfig \uc218\uc815<\/h2>\n<p>\uae30\uc874\uc5d0 <code>userDetailsService()<\/code> \ub97c \uc81c\uac70\ud569\ub2c8\ub2e4.<\/p>\n<pre><code class=\"language-java\">@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {\n    @Override\n    protected void configure(HttpSecurity http) throws Exception {\n        http\n                .authorizeRequests()\n                    .antMatchers(&quot;\/&quot;, &quot;\/home&quot;).permitAll()\n                    .anyRequest().authenticated()\n                    .and()\n                .formLogin()\n                    .loginPage(&quot;\/login&quot;)\n                    .permitAll()\n                    .and()\n                .logout()\n                    .permitAll();\n    }\n\n\/\/    @Bean\n\/\/    @Override\n\/\/    public UserDetailsService userDetailsService() {\n\/\/        UserDetails user =\n\/\/                User.withDefaultPasswordEncoder()\n\/\/                        .username(&quot;user&quot;)\n\/\/                        .password(&quot;password&quot;)\n\/\/                        .roles(&quot;USER&quot;)\n\/\/                        .build();\n\/\/\n\/\/        return new InMemoryUserDetailsManager(user);\n\/\/    }\n}<\/code><\/pre>\n<h2>\ud504\ub85c\uc81d\ud2b8 \uc2e4\ud589<\/h2>\n<p>\ud504\ub85c\uc81d\ud2b8\ub97c \uc2e4\ud589\ud558\uba74, \ub370\uc774\ud0c0\ubca0\uc774\uc2a4\uc5d0 \uc800\uc7a5\ud55c \uc544\uc774\ub514\/\ube44\ubc00\ubc88\ud638\ub97c \uc774\uc6a9\ud574 \ub85c\uadf8\uc778\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.<\/p>\n<h2>\ube44\ubc00\ubc88\ud638 \uc554\ud638\ud654<\/h2>\n<p>\ud604\uc7ac\ub294 \ube44\ubc00\ubc88\ud638\uac00 \ub370\uc774\ud0c0\ubca0\uc774\uc2a4 \uc0c1\uc5d0 \ud3c9\ubb38\uc73c\ub85c \uc800\uc7a5\ub429\ub2c8\ub2e4.<\/p>\n<p>\ubcf4\uc548\uc0c1 \ucde8\uc57d\ud558\ubbc0\ub85c \uc554\ud638\ud654\ub41c \ube44\ubc00\ubc88\ud638\uac00 \ub370\uc774\ud0c0\ubca0\uc774\uc2a4\uc5d0 \uc800\uc7a5\ub418\ub3c4\ub85d \ubc29\uc2dd\uc744 \ubcc0\uacbd\ud569\ub2c8\ub2e4.<\/p>\n<h3>\uc554\ud638\ud654 \ube44\ubc00\ubc88\ud638 \uc0dd\uc131<\/h3>\n<pre><code class=\"language-java\">public class MakeBCryptPassword {\n\n    public static void main(String[] args) {\n\n        String password = &quot;222&quot;;\n\n        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();\n        String hashedPassword = passwordEncoder.encode(password);\n        System.out.println(hashedPassword);\n        System.out.println(passwordEncoder.matches(&quot;222&quot;, hashedPassword));\n    }\n}<\/code><\/pre>\n<p>\uc704 \ud30c\uc77c\uc744 \uc2e4\ud589\ud558\uace0, \ub85c\uadf8\uc5d0 \ud45c\uc2dc\ub418\ub294 \uc554\ud638\ud654 \ube44\ubc00\ubc88\ud638\ub97c \ubcf5\uc0ac\ud574 \ub370\uc774\ud0c0\ubca0\uc774\uc2a4\uc5d0 \uc800\uc7a5\ud569\ub2c8\ub2e4.<\/p>\n<pre><code class=\"language-sql\">UPDATE tbl_user\nSET PASSWORD = &#039;$2a$10$7yXyY8.uGeP6PWs0P.yw9Ojvf94IfG9eyx75AKvTK9IDo0f3pZle6&#039;\nWHERE id = 1;<\/code><\/pre>\n<h3>MyUserDetailsService \uc218\uc815<\/h3>\n<p>MyUserDetailsService.java<\/p>\n<pre><code class=\"language-java\">@Service\npublic class MyUserDetailsService implements UserDetailsService {\n\n    @Autowired\n    private UserRepository userRepository;\n\n    private final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();\n\n    @Override\n    public UserDetails loadUserByUsername(String username) {\n        User user = userRepository.findByUsername(username);\n        if (user == null) {\n            throw new UsernameNotFoundException(username);\n        }\n        return new MyUserDetails(user);\n    }\n\n    public PasswordEncoder passwordEncoder() {\n        return this.passwordEncoder;\n    }\n}<\/code><\/pre>\n<p>WebSecurityConfig.java<\/p>\n<pre><code class=\"language-java\">@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Autowired\n    MyUserDetailsService userService;\n\n    @Override\n    protected void configure(HttpSecurity http) throws Exception {\n        http\n                .authorizeRequests()\n                    .antMatchers(&quot;\/&quot;, &quot;\/home&quot;).permitAll()\n                    .anyRequest().authenticated()\n                    .and()\n                .formLogin()\n                    .loginPage(&quot;\/login&quot;)\n                    .permitAll()\n                    .and()\n                .logout()\n                    .permitAll();\n    }\n\n    @Override\n    protected void configure(AuthenticationManagerBuilder auth) throws Exception {\n        auth.userDetailsService(userService)\n                .passwordEncoder(userService.passwordEncoder())\n        ;\n    }\n}<\/code><\/pre>\n<h3>\ud504\ub85c\uc81d\ud2b8 \uc7ac\uc2e4\ud589<\/h3>\n<p>\ud504\ub85c\uc81d\ud2b8\ub97c \uc7ac\uc2e4\ud589\ud558\uba74 \uc554\ud638\ud654\ub41c \ube44\ubc00\ubc88\ud638\ud654 \uc785\ub825\ub41c \ube44\ubc00\ubc88\ud638\ub97c \ube44\uad50\ud558\uac8c \ub429\ub2c8\ub2e4.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Spring Security with JDBC(UserDetailsService) \uc804\uccb4\uc18c\uc2a4 \uc804\uccb4\uc18c\uc2a4\ub294 \uc5ec\uae30 \uc5d0 \uacf5\uac1c\ub418\uc5b4 \uc788\uc2b5\ub2c8\ub2e4. \ud504\ub85c\uc81d\ud2b8 \uc0dd\uc131 \uc6f9\uc11c\ube44\uc2a4\uc5d0 \ubcf4\uc548 \uc801\uc6a9 \uc744 \uae30\ucd08\ub85c \uc2dc\uc791\ud569\ub2c8\ub2e4. \uc758\uc874\uc131 \ucd94\uac00 build.gradle \uc5d0 jdbc, jpa, mysql-connector \ub97c \ucd94\uac00\ud574 \uc90d\ub2c8\ub2e4. dependencies { implementation &#039;org.springframework.boot:spring-boot-starter-thymeleaf&#039; implementation &#039;org.springframework.boot:spring-boot-starter-web&#039; implementation &#039;org.springframework.boot:spring-boot-starter-security&#039; implementation &#039;org.springframework.boot:spring-boot-starter-data-jdbc&#039; implementation &#039;org.springframework.boot:spring-boot-starter-data-jpa&#039; runtimeOnly &#039;mysql:mysql-connector-java&#039; compileOnly &#039;org.projectlombok:lombok&#039; developmentOnly &#039;org.springframework.boot:spring-boot-devtools&#039; annotationProcessor &#039;org.projectlombok:lombok&#039; testImplementation &#039;org.springframework.boot:spring-boot-starter-test&#039; implementation &#039;org.springframework.security:spring-security-test&#039; } application.properties \uc124\uc815\u2026 <span class=\"read-more\"><a href=\"https:\/\/www.skyer9.pe.kr\/wordpress\/?p=2267\">Read More &raquo;<\/a><\/span><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[29],"tags":[],"class_list":["post-2267","post","type-post","status-publish","format-standard","hentry","category-spring-boot-2-5"],"_links":{"self":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/2267","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2267"}],"version-history":[{"count":13,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/2267\/revisions"}],"predecessor-version":[{"id":2291,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/2267\/revisions\/2291"}],"wp:attachment":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2267"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2267"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2267"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}