转自:http://justinrodenbostel.com/2014/05/30/part-5-integrating-spring-security-with-spring-boot-web/

PART 5: INTEGRATING SPRING SECURITY WITH SPRING BOOT WEB

Spring Boot provides utilities for quick and easy setup of Spring Security via auto-configuration and Java-based configuration. The getting started guide is quick and easy leads through configuring an in-memory
AuthenticationManager in just minutes. Going beyond these examples, this installation will quickly review the getting started guide provided at Spring.io, and conclude with the configuration
of a datasource-backed AuthenticationManager that uses Spring Data JPA, and the MySQL database platform.

As usual, for this installment, I’ve created a copy of the code from Part 4 and created a new project called Part 5. It’s committed to Github,
ready for cloning.

Updating Dependencies

To install Spring Security, we first need to update our gradle script to include a dependency on spring-boot-starter-security. Update build.gradle to include the following dependency as seen below.

/build.gradle:

1
2
3
4
5
6
7
dependencies {
    compile("org.springframework.boot:spring-boot-starter-web")
    compile("org.springframework.boot:spring-boot-starter-security")
    compile("org.thymeleaf:thymeleaf-spring4:2.1.2.RELEASE")
 
    testCompile("junit:junit")
}

Following that, executing a build should pull in our new dependencies.

Creating The Security Configuration

Continuing to lift code from the Spring.io docs for review, below you’ll find the example of the base Java security configuration. We’ll review the important bits after the jump. We’ll
create this in the same directory as our other configuration files:

/src/main/java/com.rodenbostel.sample/SecurityConfiguration.java:

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
29
30
31
32
33
34
35
36
37
packagecom.rodenbostel.sample;
 
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.security.authentication.AuthenticationManager;
importorg.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
importorg.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
importorg.springframework.security.config.annotation.web.builders.HttpSecurity;
importorg.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
importorg.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
importorg.springframework.security.web.util.matcher.AntPathRequestMatcher;
 
@Configuration
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(prePostEnabled =true)
publicclassSecurityConfiguration
extendsWebSecurityConfigurerAdapter {
 
    @Override
    protectedvoidconfigure(HttpSecurity http)
throwsException {
        http
                .authorizeRequests().anyRequest().authenticated();
        http
                .formLogin().failureUrl("/login?error")
                .defaultSuccessUrl("/")
                .loginPage("/login")
                .permitAll()
                .and()
                .logout().logoutRequestMatcher(newAntPathRequestMatcher("/logout")).logoutSuccessUrl("/login")
                .permitAll();
    }
 
    @Override
    protectedvoidconfigure(AuthenticationManagerBuilder
auth)
throwsException {
        auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
    }
}

As usual, the @Configuration annotation lets Spring know that this file contains configuration information. The next two annotations (@EnableWebMvcSecurity and @EnableGlobalMethodSecurity(prePostEnabled=true))
setup the automatically-configured portions of our security scheme, provided by Spring-Boot. EnableWebMvcSecurity basically pulls in the default SpringSecurity/SpringMVC integration. It’s an extension of theWebMvcConfigurerAdapter,
and adds methods for handling and generating CSRF tokens and resolving the logged in user, and configures default AuthenticationManagers and Pre/Post object authorization implementations. The @EnableGlobalMethodSecurity sets up processors for authorization
advice that can be added around methods and classes. This authorization advice lets a developer write Spring EL that inspects input parameters and return types.

Our SecurityConfiguration class also extends WebSecurityConfigurerAdapter.
In Spring/Spring Boot, Configurer Adapters are classes that construct default bean configurations and contain empty methods which are meant to be overridden. Overriding these methods allow a developer to customize the Web Security Configuration during startup.
Typically, the default configurations are constructed, and immediately following, the empty methods are called. If you’ve overridden an empty method, you’re able to inject custom behavior into the default configuration during the startup of the container.

In our case, the two coded parts of our SecurityConfiguration class (two methods named “configure”) are examples of these empty methods meant to be overridden. During container startup, after the HttpSecurity
object’s default configuration is specified, our overridden method is called. Here we are able to customize the default configuration by specifying which requests to authorize, and how to route various security-related requests: default success URL, error
routing, where to send logouts, etc. Also during container startup, after the AuthenticationManagerBuilder is configured, our configure method is called, and in this case we’re altering the default configuration, giving instructions to the AuthenticationManagerBuilder
to build an in-memory AuthenticationManager with a default user credential and role.

You’ll notice in this configuration we’ve specified several URL paths that do not exist. There’s no login page or controller, and no way for a user to interact with the security configuration when the app is started
up. Next, we’ll need to construct and wire in a login page to complete our beginning configuration.

Building The Login Page

The login page in the Spring.io sample is very straightforward. Just a simple form with an input for username and password. Let’s build that and review a few key parts.

/src/main/resources/templates/login.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPEhtml>
<head>
    <title>Spring Security Example</title>
</head>
<body>
<divth:if="${param.error}">
    Invalid username and password.
</div>
<divth:if="${param.logout}">
    You have been logged out.
</div>
<formth:action="@{/login}"method="post">
    <div><label>
User Name : <
input
type
="text"name="username"/>
</
label></div>
    <div><label>
Password: <
input
type
="password"name="password"/>
</
label></div>
    <div><inputtype="submit"value="Sign
In"
/></div>
</form>
</body>
</html>

Most importantly, we have inputs with the names of “username” and “password”. These are the Spring Security defaults. If you’re routing a request to Spring Security to authenticate, these are the parameters on
the request that it will be looking for. Next, you’ll also notice that there are Thymeleaf conditionals (th:if) for displaying logout and error messages if they are present in the response parameters during rendering. You’ll also notice the path to this page
is “/login”, and the action on this form routes back to “/login” – but we don’t have those registered anywhere…

Registering the Login Action

The path our login form is posting to is the default used by Spring Security. This is where what used to be called the “j_spring_security_check” servlet is listening for requests to authenticate. The request path (where we’re retrieving the login form by issuing
a GET to /login) is normally mapped to a controller, but in this case, since we’re using automatically configured features of Spring Boot, we need to specify this mapping in our application configuration. Add the code below to your application configuration.
You may notice the use of another @Override method – another hook where we can add logic to customize our application…

/src/main/java/com.rodenbostel.sample.Application.java:

1
2
3
4
@Override
publicvoidaddViewControllers(ViewControllerRegistry registry) {
    registry.addViewController("/login").setViewName("login");
}

Log In!

Start your server, and try to access the app again. For me, that’s simply visitinghttp://localhost:8080.

I’m immediately challenged.

If I put in an invalid username or password, we should see an error:

If I put in the correct credentials (id: user/password: password), we should be able to log in:

There’s quite a bit missing here still – let’s take this example a bit further – we’ll wire in components that would make this configuration closer to production ready – an AuthenticationManager backed by JDBC,
configurable password encoders, and a UserDetailsService implementation that we can use to manage users.

Beyond The Examples

To begin taking steps closer to this solution being production-ready, we first need to back our app with a database. I’ll be using MySQL. I’ll assume you’ve got it installed and running
(if you’re on a mac, I’d use Homebrew to accomplish that.

First, we’ll add the MySQL dependency to our gradle script:

/build.gradle:

1
2
3
4
5
6
7
8
9
dependencies {
    compile("org.springframework.boot:spring-boot-starter-web")
    compile("org.springframework.boot:spring-boot-starter-security")
    compile("org.springframework.boot:spring-boot-starter-data-jpa")
    compile("org.thymeleaf:thymeleaf-spring4:2.1.2.RELEASE")
    runtime('mysql:mysql-connector-java:5.1.6')
 
    testCompile("junit:junit")
}

Configuring A Datasource

I’ll be calling my schema in MySQL “beyond-the-examples”. I’ll assume you’ve used the same name. Conveniently, Spring Boot Starter projects have an automatically
configured property source path
. This means that using a properties file for configuration data we’d like to externalize simply requires creating an “application.properties” file and putting it somewhere on the application’s classpath. We’ll create that
file now, and add properties that we’ll use to set up our datasource.

/src/main/resources/application.properties:

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
spring.datasource.url=jdbc:mysql://localhost:3306/beyond-the-examples
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driverClassName=com.mysql.jdbc.Driver
 
spring.jpa.hibernate.dialect= org.hibernate.dialect.MySQLInnoDBDialect
spring.jpa.generate-ddl=false
1
 
You can see I’m using thedefaultconfiguration
forMySQL.  I wouldn’t recommend that
forproduction.
 
Next, we’ll build references to these properties in our application’s configuration, so that we can use them to create a datasource bean that we can inject into our security
configuration.  Update the application configuration file to add these properties:
 
/src/main/java/com.rodenbostel.sample.Application.java:
1
    @Value("${spring.datasource.driverClassName}")
    privateString databaseDriverClassName;
 
    @Value("${spring.datasource.url}")
    privateString datasourceUrl;
 
    @Value("${spring.datasource.username}")
    privateString databaseUsername;
 
    @Value("${spring.datasource.password}")
    privateString databasePassword;
1
 
Next create a Datasource@Beanusing these properties in the same file.
 
/src/main/java/com.rodenbostel.sample.Application.java:
1
    @Bean
    publicDataSource datasource() {
        org.apache.tomcat.jdbc.pool.DataSource ds =neworg.apache.tomcat.jdbc.pool.DataSource();
        ds.setDriverClassName(databaseDriverClassName);
        ds.setUrl(datasourceUrl);
        ds.setUsername(databaseUsername);
        ds.setPassword(databasePassword);
 
        returnds;
    }

Now, we have a datasource configured that we can @Autowire into any of our Spring beans, configuration or otherwise.

Create the Spring Security Tables

The DDL from the Spring.io docs is for HSQLDB. It’s syntax is not compliant with MySQL. Shout out to this guy (http://springinpractice.com/2010/07/06/spring-security-database-schemas-for-mysql)
for publishing the MySQL versions of the default Spring Security schema. If you’re using MySQL like me, use the DDL from that blog to create a “users” table and an “authorities” table, then thank him. Since we’ll be properly encoding our passwords, we may
want to make that password column a bit wider. Here’s what I ran:

1
createtableusers (    username
varchar(50)notnull
primary
key
,   passwordvarchar(255)notnull,   
enabled boolean
notnull) engine = InnoDb;createtableauthorities
(    username
varchar(50)notnull,    authorityvarchar(50)notnull,   foreignkey(username)referencesusers
(username),   
uniqueindex
authorities_idx_1 (username, authority)) engine = InnoDb;

Building The New Configuration

To start using the new datasource in the security configuration, we first need to wire the datasource bean into our SecurityConfiguration class. Update your SecurityConfiguration file to instruct spring to @Autowire this bean:

/src/main/java/com.rodenbostel.sample.SecurityConfiguration.java:

1
2
@Autowired
privateDataSource datasource;

Next, we’re going to make a few significant changes to our AuthenticationManagerBuilder configuration to reference this datasource and a few other things, which I’ll review after the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    @Override
    protectedvoidconfigure(AuthenticationManagerBuilder
auth)
throwsException {
        JdbcUserDetailsManager userDetailsService =newJdbcUserDetailsManager();
        userDetailsService.setDataSource(datasource);
        PasswordEncoder encoder =newBCryptPasswordEncoder();
 
        auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
        auth.jdbcAuthentication().dataSource(datasource);
 
        if(!userDetailsService.userExists("user"))
{
            List<GrantedAuthority> authorities =newArrayList<GrantedAuthority>();
            authorities.add(newSimpleGrantedAuthority("USER"));
            User userDetails =newUser("user",
encoder.encode(
"password"), authorities);
 
            userDetailsService.createUser(userDetails);
        }
    }
1
 
Prior tothis, our AuthenticationManagerBuilder was configured on a single line - we were using
an in-memory configuration, and creating a user directly on it.  Here, we’ll use the AuthenticationManagerBuilder to move from using:
 
1
auth.inMemoryAuthentication()

to using:

1
auth.jdbcAuthentication().dataSource(datasource);

Assuming there are already users in the database, believe it or not, that’s all we need to begin using the JDBC-backed AuthenticationManager. The requirement for creating new users and managing existing users
is a foregone conclusion. In our case, we’d like to automatically configure a default user on app startup just like we were before. We can get a handle on the automatically configuration (by Spring Boot) UserDetailsService through our AuthenticationManagerBuilder
at:

1
auth.getDefaultUserDetailsService();

…but that doesn’t quite do everything we need. On the first line of our updated AuthenticationManagerBuilder configuration method, you can see we’ve created a new instance of one of the provide implementations
of UserDetailsService provided by Spring. If you don’t have a reason to customize how you manage users in your system, that is a perfectly suitable implementation, but there are things to consider. Please consult the API docs for more detail (http://docs.spring.io/spring-security/site/docs/3.2.4.RELEASE/apidocs/org/springframework/security/provisioning/JdbcUserDetailsManager.html).
After creating the new reference to the JdbcUserDetailsManager, we need to set a reference to our datasource on it. Following that, we add our encoder for storing our passwords securely, and then we use the JdbcUserDetailsManager’s built-in functionality to
check to see if our test user exists, and create him if he doesn’t.

Testing Again

Running the application should yield no change in behavior when compared with what we saw earlier. This is desired. What we will see that’s different will be in our database. Startup the app using: “gradle bootRun”, and using your favorite database management
tool, query the database to see our newly create user and their encoded password:

Conclusion

I cobbled the information in this post from many sources – some I’ve remembered and have mentioned, and others I have not. I hope putting this information in a single post helps whoever stumbles upon it! That concludes this series of Spring Boot posts, but
during the time I’ve been writing these, I’ve come up with two more topics to touch on, mostly surrounding further securing your app (http://www.jasypt.org/) and easier maintenance
of your database tables (http://flywaydb.org/). Check back soon!

PART 5: INTEGRATING SPRING SECURITY WITH SPRING BOOT WEB的更多相关文章

  1. Keycloak 团队宣布他们正在弃用大多数 Keycloak 适配器,包括Spring Security和Spring Boot

    2月14日,Keycloak 团队宣布他们正在弃用大多数 Keycloak 适配器. 其中包括Spring Security和Spring Boot的适配器,这意味着今后Keycloak团队将不再提供 ...

  2. Spring Security +Oauth2 +Spring boot 动态定义权限

    Oauth2介绍:Oauth2是为用户资源的授权定义了一个安全.开放及简单的标准,第三方无需知道用户的账号及密码,就可获取到用户的授权信息,并且这是安全的. 简单的来说,当用户登陆网站的时候,需要账号 ...

  3. 漫谈Spring Security 在Spring Boot 2.x endpoints中的应用(一)

    Spring Boot 2.x极大简化了默认的安全配置,并不是说有很多安全相关的配置,现在你只需要提供一个WebSecurityConfigurerAdapter继承类这样一个简单的操作,Spring ...

  4. spring boot:用spring security加强spring boot admin的安全(spring boot admin 2.3.0 / spring boot 2.3.3)

    一,spring boot admin的安全环节: 1,修改context-path,默认时首页就是admin, 我们修改这个地址可以更安全 2,配置ip地址白名单,有ip限制才安全, 我们使用了sp ...

  5. 微服务下前后端分离的统一认证授权服务,基于Spring Security OAuth2 + Spring Cloud Gateway实现单点登录

    1.  整体架构 在这种结构中,网关就是一个资源服务器,它负责统一授权(鉴权).路由转发.保护下游微服务. 后端微服务应用完全不用考虑权限问题,也不需要引入spring security依赖,就正常的 ...

  6. SPRING SECURITY JAVA配置:Web Security

    在前一篇,我已经介绍了Spring Security Java配置,也概括的介绍了一下这个项目方方面面.在这篇文章中,我们来看一看一个简单的基于web security配置的例子.之后我们再来作更多的 ...

  7. spring security learning(spring in action)

    1.使用Spring Security配置命名空间 spring securtiy 提供了安全性相关的命名空间,我们可以将spring security的命名空间声明添加到spring公用的配置xml ...

  8. spring security在spring mvc的action中获取登录人信息

    @RequestMapping("/index") public ModelAndView login( @RequestParam(value = "error&quo ...

  9. 【Spring】关于Boot应用中集成Spring Security你必须了解的那些事

    Spring Security Spring Security是Spring社区的一个顶级项目,也是Spring Boot官方推荐使用的Security框架.除了常规的Authentication和A ...

随机推荐

  1. 从命令行配置 Windows 防火墙

    从命令行配置 Windows 防火墙 高级用户可以使用命令行来配置 Windows 防火墙.您可以使用 netsh 命令行工具来进行配置. 下表中的 netsh 命令可用于 Microsoft Win ...

  2. [SDOI2008]沙拉公主的困惑 线性筛 素数+欧拉

    本文为博主原创文章,欢迎转载,请注明出处 www.cnblogs.com/yangyaojia [SDOI2008]沙拉公主的困惑 线性筛 素数+欧拉 题目大意 给定n,m,求在1到n!内与m!互质的 ...

  3. Arcengine设置坐标系

    转自原文 Arcengine设置坐标系 ArcGIS Engine提供了一系列对象供开发者管理GIS系统的坐标系统. 对大部分开发者而言了解ProjectedCoordinateSystem, Geo ...

  4. UVA 10039 Railroads

    这道题好吧,一开始便是拓扑排序的想法,搞了好久,试了多组测试数据,没错啊,可是没过...作孽啊,竟然忘了拓扑不能处理环,白浪费了一晚上... 只好用动态规划了.. DP[time][city]表示在t ...

  5. WinForm使用CefSharp内嵌chrome浏览器

    先贴运行图:亲测可用!以图为证! 开始!1.创建winform程序,使用.NET 4.5.2或以上(vs2010最高支持.NET 4.0,我使用的是vs2017).这一步容易忽略,简单的说就是将项目. ...

  6. [Wikioi 1226]倒水问题

    题目描写叙述 Description 有两个无刻度标志的水壶.分别可装 x 升和 y 升 ( x,y 为整数且均不大于 100 )的水. 设另有一水 缸,可用来向水壶灌水或接从水壶中倒出的水, 两水壶 ...

  7. POJ 1442 Black Box(优先队列)

    题目地址:POJ 1442 这题是用了两个优先队列,当中一个是较大优先.还有一个是较小优先. 让较大优先的队列保持k个.每次输出较大优先队列的队头. 每次取出一个数之后,都要先进行推断,假设这个数比較 ...

  8. c++11 实现半同步半异步线程池

    感受: 随着深入学习,现代c++给我带来越来越多的惊喜- c++真的变强大了. 半同步半异步线程池: 事实上非常好理解.分为三层 同步层:通过IO复用或者其它多线程多进程等不断的将待处理事件加入到队列 ...

  9. Linux - 理不清的权限chmod与chown区别

    chmod是修改第一列内容的 ,chown是修改第3,4列内容的. [root@local ~]# chmod 777 -R add.sh [root@local ~]# chown jiqing:j ...

  10. Uva 11754(枚举+中国剩余定理)

    #include<iostream> #include<cstdio> #include<cmath> #include<cstring> #inclu ...