SpringBoot整合SpringSecurity 入门篇超级简单

本文展示了如何在SpringBoot中整合SpringSecurity,使用JWT进行授权管理。通过添加依赖,配置秘钥,实现UserDetailsServiceImpl和AuthenticationEntryPointImpl,自定义WebSecurityConfig,创建TokenController来获取和验证token。在请求中携带token可以成功访问受保护的资源,否则会收到未授权的响应。文章还提及后续将针对此基础版本进行优化和增强。

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

1.pom添加jar包引用

 <dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
  1. resouces 添加秘钥文件和配置修改
    随机生成的RSA公钥,私钥保存到对应文件
    公钥/私钥
    application.properties增加配置
jwt.private.key=classpath:app.key
jwt.public.key=classpath:app.pub

3.UserDetailsServiceImpl 实现类

实现UserDetailsService接口,主要作用是实现应用自己的用户查询逻辑,和数据库关联获取对应的User,组装UserDetails返回

@Service
@AllArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {

    private UserDao userDao;

    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        User user = userDao.selectOne(Wrappers.<User>query().lambda().eq(User::getUserName, userName));
        if (user==null){
            throw new UsernameNotFoundException(userName);
        }

        return getUserDetails(user);
    }

    private UserDetails getUserDetails(User user){
        return org.springframework.security.core.userdetails.User.
                withUsername(user.getUserName()).
                password("{noop}"+user.getPassword()).
                authorities("web","app").build();
    }
}

4. AuthenticationEntryPointImpl 错误处理类

实现 AuthenticationEntryPoint 接口,主要作用是自定义Spring Security的异常处理,否则异常返回到前端的可读性太差。

@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable {

    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        throw e;
    }
}

5. WebSecurityConfig 配置信息

  1. 继承 WebSecurityConfigurerAdapter,添加@Configuration,@EnableWebSecurity 注解
  2. 重写config()方法,配置授权相关信息,指定免校验url,指定授权模式为JWT,指定异常处理handler
  3. 指定provider,管理第1步实现的 UserDetailsService
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	AuthenticationEntryPointImpl authenticationEntryPoint;

	@Autowired
	UserDetailsServiceImpl userDetailsService;

	@Value("${jwt.public.key}")
	RSAPublicKey key;

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		// @formatter:off
		http.authorizeRequests((authz) -> authz.anyRequest().authenticated())
			.csrf((csrf) -> csrf.ignoringAntMatchers("/token"))
			.httpBasic(Customizer.withDefaults())
			.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
			.sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
			.exceptionHandling((exceptions) -> exceptions
				.authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
				.accessDeniedHandler(new BearerTokenAccessDeniedHandler()).authenticationEntryPoint(authenticationEntryPoint)
			);
	}

	@Bean
    JwtDecoder jwtDecoder() {
		return NimbusJwtDecoder.withPublicKey(this.key).build();
	}

	@Bean
	public AuthenticationManager getManager(){
		DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
		provider.setUserDetailsService(userDetailsService);
		ProviderManager manager = new ProviderManager(provider);
		return manager;
	}
}

6. 定义TokenController 增加获取token的接口

主要定义token的过期时间,scop范围,当前登录的用户信息等,返回token

@RestController
public class TokenController {

	@Value("${jwt.private.key}")
	RSAPrivateKey key;

	@PostMapping("/token")
	public String token(Authentication authentication) {
		Instant now = Instant.now();
		long expiry = 36000L;
		// @formatter:off
		String scope = authentication.getAuthorities().stream()
				.map(GrantedAuthority::getAuthority)
				.collect(Collectors.joining(" "));
		JWTClaimsSet claims = new JWTClaimsSet.Builder()
				.issuer("self")
				.issueTime(new Date(now.toEpochMilli()))
				.expirationTime(new Date(now.plusSeconds(expiry).toEpochMilli()))
				.subject(authentication.getName())
				.claim("scope", scope)
				.build();
		// @formatter:on
		JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS256).build();
		SignedJWT jwt = new SignedJWT(header, claims);
		return sign(jwt).serialize();
	}

	SignedJWT sign(SignedJWT jwt) {
		try {
			jwt.sign(new RSASSASigner(this.key));
			return jwt;
		}
		catch (Exception ex) {
			throw new IllegalArgumentException(ex);
		}
	}
}

7.发起请求获取token

basic base64(username:password) 放在请求头里
在这里插入图片描述
发起请求获取token如下图,
在这里插入图片描述

8.发起普通请求不带token

发起get请求,url=localhost:8080/user/getUserById?id=2
返回结果,提示没有得到授权

{
    "timestamp": "2021-07-09T13:24:33.821+00:00",
    "status": 401,
    "error": "Unauthorized",
    "path": "/user/getUserById"
}

9.发起普通请求带上token

在这里插入图片描述
返回正确结果

{
    "code": 200,
    "data": {
        "id": 1,
        "userName": "xiaowang",
        "name": "小王",
        "password": "password",
        "age": 20,
        "sex": 1,
        "status": 1,
        "createTime": "2021-06-10T08:50:29.000+00:00"
    }
}

总结

  1. 自此springboot整合sprignsecurity的入门版本就跑通了,是不是很简单呢
  2. 后续我们会针改版本做深入的优化,增加密码加密、密码校验、token的作废、退出功能等方面
  3. 功能完善会对springsecurity的原理做一些剖析

源码地址
上一篇 SpringBoot整合validation校验

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值