INTEGRATING SPRING SECURITY WITH SPRING BOOT WEB

本文介绍如何使用 SpringBoot 快速配置 SpringSecurity,并通过示例演示如何从内存认证过渡到基于 MySQL 数据库的认证管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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
package com.rodenbostel.sample;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
 
@Configuration
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity (prePostEnabled = true )
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
 
     @Override
     protected void configure(HttpSecurity http) throws Exception {
         http
                 .authorizeRequests().anyRequest().authenticated();
         http
                 .formLogin().failureUrl( "/login?error" )
                 .defaultSuccessUrl( "/" )
                 .loginPage( "/login" )
                 .permitAll()
                 .and()
                 .logout().logoutRequestMatcher( new AntPathRequestMatcher( "/logout" )).logoutSuccessUrl( "/login" )
                 .permitAll();
     }
 
     @Override
     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
         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 the WebMvcConfigurerAdapter, 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
<! DOCTYPE html>
< head >
     < title >Spring Security Example</ title >
</ head >
< body >
< div th:if = "${param.error}" >
     Invalid username and password.
</ div >
< div th:if = "${param.logout}" >
     You have been logged out.
</ div >
< form th: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 >< input type = "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
public void addViewControllers(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.

Screen Shot 2014-05-30 at 4.59.05 PM

I’m immediately challenged.

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

Screen Shot 2014-05-30 at 4.59.27 PM

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

Screen Shot 2014-05-30 at 5.00.33 PM

Screen Shot 2014-05-30 at 5.00.38 PM

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
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

You can see I’m using the default configuration for MySQL. I wouldn’t recommend that for production.

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
2
3
4
5
6
7
8
9
10
11
@Value ( "${spring.datasource.driverClassName}" )
private String databaseDriverClassName;
 
@Value ( "${spring.datasource.url}" )
private String datasourceUrl;
 
@Value ( "${spring.datasource.username}" )
private String databaseUsername;
 
@Value ( "${spring.datasource.password}" )
private String databasePassword;

Next create a Datasource @Bean using these properties in the same file.

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

1
2
3
4
5
6
7
8
9
10
@Bean
public DataSource datasource() {
     org.apache.tomcat.jdbc.pool.DataSource ds = new org.apache.tomcat.jdbc.pool.DataSource();
     ds.setDriverClassName(databaseDriverClassName);
     ds.setUrl(datasourceUrl);
     ds.setUsername(databaseUsername);
     ds.setPassword(databasePassword);
 
     return ds;
}

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
create table users (    username varchar (50) not null primary key ,    password varchar (255) not null ,    enabled boolean not null ) engine = InnoDb; create table authorities (    username varchar (50) not null ,    authority varchar (50) not null ,    foreign key (username) references users (username),    unique index 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
private DataSource 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
     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
         JdbcUserDetailsManager userDetailsService = new JdbcUserDetailsManager();
         userDetailsService.setDataSource(datasource);
         PasswordEncoder encoder = new BCryptPasswordEncoder();
 
         auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
         auth.jdbcAuthentication().dataSource(datasource);
 
         if (!userDetailsService.userExists( "user" )) {
             List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
             authorities.add( new SimpleGrantedAuthority( "USER" ));
             User userDetails = new User( "user" , encoder.encode( "password" ), authorities);
 
             userDetailsService.createUser(userDetails);
         }
     }
 
 
Prior to this , 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:
 
 
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:

Screen Shot 2014-05-30 at 5.44.38 PM

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!

基于C#开发的一个稳定可靠的上位机系统,旨在满足工业控制的需求。该系统集成了多个功能界面,如操作界面、监控界面、工艺流显示界面、工艺表界面、工艺编辑界面、曲线界面和异常报警界面。每个界面都经过精心设计,以提高用户体验和工作效率。例如,操作界面和监控界面对触摸屏友好,支持常规点击和数字输入框;工艺流显示界面能够实时展示工艺步骤并变换颜色;工艺表界面支持Excel和加密文件的导入导出;工艺编辑界面采用树形编辑方式;曲线界面可展示八组曲线并自定义纵坐标数值;异常报警界面能够在工艺流程出现问题时及时报警。此外,该系统还支持与倍福TC2、TC3和西门子PLC1200/300等下位机设备的通信,确保生产线的顺畅运行。系统参考欧洲工艺软件开发,已稳定运行多年,证明了其可靠性和稳定性。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是对C#编程有一定基础的人群。 使用场景及目标:适用于需要构建高效、稳定的工业控制系统的企业和个人开发者。主要目标是提升生产效率、确保生产安全、优化工艺流程管理和实现数据的有效管理与传输。 其他说明:文中提供了部分示例代码片段,帮助读者更好地理解具体实现方法。系统的复杂度较高,但凭借C#的强大功能和开发团队的经验,确保了系统的稳定性和可靠性。
《可靠UDP:.NET实现与应用详解》 在计算机网络通信中,TCP(Transmission Control Protocol)因其面向连接、可靠的数据传输特性而被广泛使用。然而,对于某些对实时性要求极高的应用场景,如在线游戏、视频会议等,TCP 的高延迟和拥塞控制策略可能并不适用。此时,UDP(User Datagram Protocol)由于其无连接、轻量级的特性成为首选,但UDP的不可靠性又成为一大挑战。为了解决这一问题,"ReliableUdp"应运而生,它是一种基于.NET实现的可靠UDP协议,旨在提供类似于TCP的可靠性,同时保持UDP的高效性。 我们要理解什么是可靠UDP。可靠UDP是在UDP基础上添加了一层机制,以确保数据包能够按序、无丢失地送达目标,同时还保持了UDP的低延迟特性。ReliableUdp库提供了这样的功能,它通过序列化、确认机制、重传策略等技术手段,实现了在UDP上构建的可靠数据传输。 1. 序列化:每个发送的数据包都会被赋予一个唯一的序列号,接收端根据序列号进行排序,确保数据包的顺序正确。 2. 确认机制:接收端在接收到数据包后,会返回一个确认消息,表明已成功接收。发送端在未收到确认时,将重发该数据包,直到收到确认或者达到重传上限。 3. 重传策略:除了简单的超时重传,ReliableUdp可能还采用了更复杂的策略,如快速重传、选择性重传等,以提高效率并减少不必要的等待。 4. 异步处理:作为完全异步的库,ReliableUdp在设计上充分利用了.NET的异步编程模型,使得应用程序可以在等待数据传输的同时执行其他任务,提高了系统资源利用率。 5. 友好的API:ReliableUdp库提供了易于使用的接口,使得开发者可以快速集成到自己的项目中,无需深入理解底层实现细节,降低了开发难度。 在实际应用中,可靠UDP特别适用于那些对实时性要求高、数据丢失容忍度低的场景,如多人在线游戏、实时音频视频传输等。通过.NET的实现,它能够无缝对接.NET生态中的各种组件和服务,为开发提供了极大的便利。 ReliableUdp是.NET环境下实现可靠UDP通信的一个优秀工具,它结合了UDP的高效性和TCP的可靠性,为需要高性能、低延迟且需保证数据完整性的应用提供了理想解决方案。开发者可以借助其提供的友好API,轻松地在项目中构建可靠的UDP通信机制,提升系统性能和用户体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值