Spring Security OAuth2密码模式(四)

写在前面:各位看到此博客的小伙伴,如有不对的地方请及时通过私信我或者评论此博客的方式指出,以免误人子弟。多谢!如果我的博客对你有帮助,欢迎进行评论✏️✏️、点赞👍👍、收藏⭐️⭐️,满足一下我的虚荣心💖🙏🙏🙏 。

  之前已经使用客户端模式进行认证授权的演示记录,但到目前为止还没有使用到用户相关的信息,之前项目都是在用户登录的时候判断用户名和密码是否正确,都校验通过后查询用户拥有的角色及菜单,并将用户权限信息放入session,那使用oauth2后怎么结合用户的角色权限保护资源呢?本篇记录一下密码模式的使用姿势。

目录

使用流程

WebSecurityConfigurerAdapter中配置AuthenticationManager

AuthorizationServerEndpointsConfigurer中配置AuthenticationManager

WebSecurityConfigurerAdapter中配置AuthenticationManagerBuilder

测试

测试类

密码模式获取token

访问资源


使用流程

大致需要修改的如下:

一、WebSecurityConfigurerAdapter中配置AuthenticationManager。

二、AuthorizationServerEndpointsConfigurer中配置AuthenticationManager。

三、WebSecurityConfigurerAdapter中配置AuthenticationManagerBuilder。

WebSecurityConfigurerAdapter中配置AuthenticationManager

在WebSecurityConfig类中增加配置AuthenticationManager这个Bean,代码如下:

@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
}

AuthorizationServerEndpointsConfigurer中配置AuthenticationManager

想要使用密码验证模式,需要在授权服务器中的AuthorizationServerEndpointsConfigurer中配置authenticationManager,因为默认情况下开启了除了密码类型外的所有验证类型,密码验证只有配置了authenticationManager的配置才会开启。

授权服务器中注入authenticationManager,endpoints中配置authenticationManager后,完整代码如下:

@EnableAuthorizationServer
@Configuration
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private DataSource dataSource;
    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.allowFormAuthenticationForClients();
        security.checkTokenAccess("permitAll()");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetails());
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST);
        endpoints.tokenStore(tokenStore());
        endpoints.authenticationManager(authenticationManager);
    }

    @Bean
    public ClientDetailsService clientDetails() {
        return new JdbcClientDetailsService(dataSource);
    }

    @Bean
    public TokenStore tokenStore() {
        return new JdbcTokenStore(dataSource);
    }

}

WebSecurityConfigurerAdapter中配置AuthenticationManagerBuilder

经过以上两步配置已经开启了密码验证模式,那怎么知道获取token时填写的用户名和密码是否正确呢?那就需要在WebSecurityConfigurerAdapter中配置AuthenticationManagerBuilder(暂且称它为:认证管理器),AuthenticationManagerBuilder并不真正验证用户信息,真正执行认证需要设置userDetailsService(一个实现UserDetailsService接口的类),另外还需要设置密码加密方式,追加代码如下:

@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}

完整代码如下:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

}

新建CustomUserDetailsService类实现UserDetailsService接口,真正的用户信息验证及验证用户名密码成功后给用户赋权都在这里,代码如下:

@Component
public class CustomUserDetailsService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority("ROLE_admin");
        List<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(simpleGrantedAuthority);
        return new User(username, "$2a$10$bowh1hF9Zwda/O5dUciQde3W6Rk88EP25xC9K9kveFnqFTRFg1n1O", authorities);
    }

    public static void main(String[] args) {
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        String password = bCryptPasswordEncoder.encode("123456");
        System.out.println(password);
    }
}

以上代码仅仅是测试用的,测试数据:用户名随意,密码123456,使用BCryptPasswordEncoder方式加密后为: $2a$10$bowh1hF9Zwda/O5dUciQde3W6Rk88EP25xC9K9kveFnqFTRFg1n1O, 用户的权限设置为ROLE_admin,真正使用的时候用户名密码还有权限信息这些应该是从数据库中查询或者调用远程服务获取的用户相关信息。

注意oauth2在验证权限的时候会在权限字符串前自动加上 “ROLE_”,所以我们构建authorities时需要自己把ROLE_追加上。

测试

测试类

新增测试类,代码如下:

@RestController
@RequestMapping("/pwd")
public class TestPwdController {

    @RequestMapping("/test1")
    @PreAuthorize("hasRole('admin')")
    public String test1() {
        return "admin";
    }

    @RequestMapping("/test2")
    @PreAuthorize("hasRole('common')")
    public String test2() {
        return "common";
    }

}

密码模式获取token

使用get、post方式获取,如下:

localhost:9004/oauth/token?client_id=pwd-client&client_secret=123456&grant_type=password&scopes=all&username=zhangsan&password=123456

注意修改下grant_type为password。

访问资源

用户权限为admin,分别访问资源/test1、/test2,/test1需要admin权限才能访问,/test2需要common权限才能访问,测试如下:

携带token访问 localhost:9004/pwd/test1 可以访问。

携带token访问 localhost:9004/pwd/test2 不可访问,状态返回403,错误信息如下:

{

    "error": "access_denied",

    "error_description": "不允许访问"

}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
要自定义Spring Security OAuth2密码模式的登录路径,你可以使用以下步骤: 1. 创建自定义登录页面和控制器,例如/login/custom。 2. 在Spring Security配置类中,使用formLogin()方法启用表单登录,并使用loginPage()方法指定登录页面的路径和permitAll()方法允许所有用户访问登录页面。 ``` @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login/custom") .permitAll() .and() .oauth2Login() .and() .oauth2Client(); } } ``` 3. 在控制器中,编写处理登录请求的方法,并使用AuthenticationManager.authenticate()方法进行身份验证。如果身份验证成功,重定向到请求的资源,否则返回登录页面。 ``` @Controller public class LoginController { @Autowired private AuthenticationManager authenticationManager; @GetMapping("/login/custom") public String showLoginForm() { return "login"; } @PostMapping("/login/custom") public String submitLoginForm(@RequestParam String username, @RequestParam String password, HttpServletRequest request, HttpServletResponse response) { UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password); try { Authentication authentication = authenticationManager.authenticate(token); SecurityContextHolder.getContext().setAuthentication(authentication); return "redirect:" + request.getParameter("redirect_uri"); } catch (AuthenticationException e) { return "login"; } } } ``` 4. 在OAuth2客户端配置中,使用loginPage()方法指定登录页面的路径。 ``` @Configuration @EnableOAuth2Client public class OAuth2ClientConfig { @Value("${security.oauth2.client.client-id}") private String clientId; @Value("${security.oauth2.client.client-secret}") private String clientSecret; @Value("${security.oauth2.client.access-token-uri}") private String accessTokenUri; @Value("${security.oauth2.client.user-authorization-uri}") private String userAuthorizationUri; @Value("${security.oauth2.client.redirect-uri}") private String redirectUri; @Value("${security.oauth2.client.scope}") private String scope; @Bean public OAuth2ProtectedResourceDetails oauth2ProtectedResourceDetails() { ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails(); details.setClientId(clientId); details.setClientSecret(clientSecret); details.setAccessTokenUri(accessTokenUri); details.setScope(Arrays.asList(scope.split(","))); return details; } @Bean public OAuth2RestTemplate oauth2RestTemplate() { OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(oauth2ProtectedResourceDetails()); restTemplate.setMessageConverters(Arrays.asList(new MappingJackson2HttpMessageConverter())); return restTemplate; } @Bean public OAuth2ClientContext oauth2ClientContext() { return new DefaultOAuth2ClientContext(new DefaultAccessTokenRequest()); } @Bean public AuthorizationCodeResourceDetails authorizationCodeResourceDetails() { AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails(); details.setClientId(clientId); details.setClientSecret(clientSecret); details.setAccessTokenUri(accessTokenUri); details.setUserAuthorizationUri(userAuthorizationUri); details.setScope(Arrays.asList(scope.split(","))); details.setUseCurrentUri(false); details.setPreEstablishedRedirectUri(redirectUri); details.setAuthenticationScheme(AuthenticationScheme.query); return details; } @Bean public OAuth2RestOperations oauth2RestOperations() { return new OAuth2RestTemplate(authorizationCodeResourceDetails(), oauth2ClientContext()); } @Bean public OAuth2ClientAuthenticationProcessingFilter oauth2ClientAuthenticationProcessingFilter() { OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter("/login/custom"); OAuth2RestTemplate restTemplate = oauth2RestTemplate(); restTemplate.setAccessTokenProvider(new AuthorizationCodeAccessTokenProvider()); filter.setRestTemplate(restTemplate); UserInfoTokenServices tokenServices = new UserInfoTokenServices(userInfoUri(), clientId); tokenServices.setRestTemplate(restTemplate); filter.setTokenServices(tokenServices); return filter; } @Bean public FilterRegistrationBean oauth2ClientFilterRegistration(OAuth2ClientAuthenticationProcessingFilter filter) { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(filter); registration.setOrder(-100); return registration; } @Value("${security.oauth2.client.user-info-uri}") private String userInfoUri; @Bean public String userInfoUri() { return userInfoUri; } } ``` 这些步骤可以帮助你自定义Spring Security OAuth2密码模式的登录路径。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值