使用Spring Security的OAuth2功能接入QQ登录

简介: 得益于Spring Security对OAuth2标准认证流程的封装,最简配置客户端也可以很方便地接入Google和Facebook等实现相对标准的OAuth2服务提供商。而对于QQ登录等不够标准的OAuth2流程来说,我们则可以在此基础上进行额外的适配工作,Spring Security良好的OAuth2扩展性同样为我们的适配提供了足够的支持。

在前一个章节中我们使用最简配置实现了OAuth2客户端接入Github登录的功能,得益于Spring Security对OAuth2标准认证流程的封装,最简配置客户端也可以很方便地接入Google和Facebook等实现相对标准的OAuth2服务提供商。而对于QQ登录等不够标准的OAuth2流程来说,我们则可以在此基础上进行额外的适配工作,Spring Security良好的OAuth2扩展性同样为我们的适配提供了足够的支持。

针对Spring Security的OAuth2功能扩展流程

不同OAuth服务商提供的授权流程在细节上可能不同,这主要体现在与授权服务器交互过程中的传参、返回值解析,以及从资源服务器获取资源两个方面。但核心步骤上大致是相同的,都是OAuth标准所制定的形式:

  1. 获取code。
  2. 使用code交换access_token。
  3. 携带access_token请求被保护的用户信息和其它资源。

针对这3个核心步骤,Spring Security提供了相应的扩展接口和配置方法:

  1. 支持自定义重定向端点(Redirection Endpoint),OAuth2服务器通过重定向到此端点的方式将code传递给OAuth2客户端。
  2. 支持自定义OAuth2AccessTokenResponseClient,OAuth2AccessTokenResponseClient负责使用code交换access_token的具体逻辑。
  3. 支持自定义用户信息端点(UserInfo Endpoint),常用自定义方式:

    1. 自定义OAuth2User。不同OAuth2服务提供商的用户属性不同,可以针对不同的OAuth2服务商做适配。
    2. 自定义OAuth2UserService。OAuth2UserService负责请求用户信息(OAuth2User)。标准OAuth2协议可以直接携带access_token请求用户信息,而QQ则需要先获取OpenId,再使用OpenId获取用户信息。

考虑到我们在对接社交账号登录功能时,一般不会局限于单个OAuth服务商,而是同时提供多种流行的社交平台供用户选择,所以需要有多套OAuth方案并存的准备。为了避免项目中不同OAuth对接代码混乱的情况,推荐使用图1这种组织形式:

image

准备工作

在使用Spring Social对接QQ登录时,基本的准备工作已经都有提及,可以直接参考该部分内容。

编码实现

相对于标准的OAuth2授权码模式,QQ提供的API在交互上较为混乱,其响应类型为text/html,响应内容则同时存在普通文本、JSONP、JSON字符串等多种类型。
另外,QQ提供的API还需要先获取OpenId,再使用OpenId结合appId与access_token的方式来获取用户信息,而不是直接使用access_token,这些都是我们需要自定义实现的重点内容。

1. 新建项目

首先,新建Spring Boot 2.0工程,命名为client-social,引入spring-boot-starter-web和spring-boot-starter-security两个依赖包:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

引入Spring Security对OAuth2实现支持的依赖包:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-jose</artifactId>
</dependency>

2. 自定义QQUserInfo实现OAuth2User接口

QQ用户信息无法使用默认的DefaultOAuth2User表示,需要提供一个自定义的QQUserInfo类并实现OAuth2User接口:

public class QQUserInfo implements OAuth2User {

    // 统一赋予USER角色
    private List<GrantedAuthority> authorities =
            AuthorityUtils.createAuthorityList("ROLE_USER");
    private Map<String, Object> attributes;

    private String nickname;
    @JsonProperty("figureurl")
    private String figureUrl30;
    @JsonProperty("figureurl_1")
    private String figureUrl50;
    @JsonProperty("figureurl_2")
    private String figureUrl100;
    @JsonProperty("figureurl_qq_1")
    private String qqFigureUrl40;
    @JsonProperty("figureurl_qq_2")
    private String qqFigureUrl100;
    private String gender;
    // 携带openId备用
    private String openId;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return this.authorities;
    }

    @Override
    public Map<String, Object> getAttributes() {
        if (this.attributes == null) {
            this.attributes = new HashMap<>();
            this.attributes.put("nickname", this.getNickname());
            this.attributes.put("figureUrl30", this.getFigureUrl30());
            this.attributes.put("figureUrl50", this.getFigureUrl50());
            this.attributes.put("figureUrl100", this.getFigureUrl100());
            this.attributes.put("qqFigureUrl40", this.getQqFigureUrl40());
            this.attributes.put("qqFigureUrl100", this.getQqFigureUrl100());
            this.attributes.put("gender", this.getGender());
            this.attributes.put("openId", this.getOpenId());
        }
        return attributes;
    }
    
    @Override
    public String getName() {
        return this.nickname;
    }
    
    // 省略setter getter 
}

3. 添加RestTamplate解析模板

public class JacksonFromTextHtmlHttpMessageConverter extends MappingJackson2HttpMessageConverter {

    // 添加对text/html的支持
    public JacksonFromTextHtmlHttpMessageConverter() {
        List mediaTypes = new ArrayList();
        mediaTypes.add(MediaType.TEXT_HTML);
        setSupportedMediaTypes(mediaTypes);
    }

}
/**
 * 由于与QQ接口的交互上,响应类型都为text/html的形式,且RestTemplate没有默认支持该解析模型,所以应当自行添加。
 * 主要是两类,一类text/html转普通文本,一类则是text/html转JSON对象。
 */
public class TextHtmlHttpMessageConverter extends AbstractHttpMessageConverter {


    public TextHtmlHttpMessageConverter() {
        super(Charset.forName("UTF-8"), new MediaType[]{MediaType.TEXT_HTML});
    }

    @Override
    protected boolean supports(Class clazz) {
        return String.class == clazz;
    }

    @Override
    protected Object readInternal(Class aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException {
        Charset charset = this.getContentTypeCharset(httpInputMessage.getHeaders().getContentType());
        return StreamUtils.copyToString(httpInputMessage.getBody(), charset);
    }

    @Override
    protected void writeInternal(Object o, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException {
    }

    private Charset getContentTypeCharset(MediaType contentType) {
        return contentType != null && contentType.getCharset() != null ? contentType.getCharset() : this.getDefaultCharset();
    }

}

4. 实现OAuth2AccessTokenResponseClient

OAuth2AccessTokenResponseClient负责使用code交换access_token的具体逻辑。默认提供的实现类NimbusAuthorizationCodeTokenResponseClient用于处理标准的OAuth2交换access_token逻辑,但QQ提供的方式并不标准,所以需要自定义实现OAuth2AccessTokenResponseClient接口:

public class QQOAuth2AccessTokenResponseClient implements OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> {

    private RestTemplate restTemplate;

    private RestTemplate getRestTemplate() {
        if (restTemplate == null) {
            restTemplate = new RestTemplate();
            restTemplate.getMessageConverters().add(new TextHtmlHttpMessageConverter());
        }

        return restTemplate;
    }

    @Override
    public OAuth2AccessTokenResponse getTokenResponse(OAuth2AuthorizationCodeGrantRequest authorizationGrantRequest)
            throws OAuth2AuthenticationException {
        ClientRegistration clientRegistration = authorizationGrantRequest.getClientRegistration();
        OAuth2AuthorizationExchange oAuth2AuthorizationExchange = authorizationGrantRequest.getAuthorizationExchange();

        // 根据API文档获取请求access_token参数
        MultiValueMap<String, String> params = new LinkedMultiValueMap();
        params.set("client_id", clientRegistration.getClientId());
        params.set("client_secret", clientRegistration.getClientSecret());
        params.set("code", oAuth2AuthorizationExchange.getAuthorizationResponse().getCode());
        params.set("redirect_uri", oAuth2AuthorizationExchange.getAuthorizationRequest().getRedirectUri());
        params.set("grant_type", "authorization_code");
        String tmpTokenResponse = getRestTemplate().postForObject(clientRegistration.getProviderDetails().getTokenUri(), params, String.class);

        // 从API文档中可以轻易获知解析accessToken的方式
        String[] items = tmpTokenResponse.split("&");
        //http://wiki.connect.qq.com/使用authorization_code获取access_token
        //access_token=FE04************************CCE2&expires_in=7776000&refresh_token=88E4************************BE14
        String accessToken = items[0].substring(items[0].lastIndexOf("=") + 1);
        Long expiresIn = new Long(items[1].substring(items[1].lastIndexOf("=") + 1));

        Set<String> scopes = new LinkedHashSet<>(oAuth2AuthorizationExchange.getAuthorizationRequest().getScopes());
        Map<String, Object> additionalParameters = new LinkedHashMap<>();
        OAuth2AccessToken.TokenType accessTokenType = OAuth2AccessToken.TokenType.BEARER;

        return OAuth2AccessTokenResponse.withToken(accessToken)
                .tokenType(accessTokenType)
                .expiresIn(expiresIn)
                .scopes(scopes)
                .additionalParameters(additionalParameters)
                .build();
    }
}

主要是使用RestTemplate请求获取access_token,并对返回的结果执行自定义解析,最后构建成OAuth2AccessTokenResponse对象返回即可。

5. 实现OAuth2UserService接口

OAuth2UserService负责请求用户信息(OAuth2User),标准的OAuth2协议可以直接携带access_token请求用户信息,但QQ还需要获取到OpenId才能使用:

public class QQOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
    // 获取用户信息的API
    private static final String QQ_URL_GET_USER_INFO = "https://graph.qq.com/user/get_user_info?oauth_consumer_key={appId}&openid={openId}&access_token={access_token}";

    private RestTemplate restTemplate;

    private RestTemplate getRestTemplate() {
        if (restTemplate == null) {
            restTemplate = new RestTemplate();
            //通过Jackson JSON processing library直接将返回值绑定到对象
            restTemplate.getMessageConverters().add(new JacksonFromTextHtmlHttpMessageConverter());
        }

        return restTemplate;
    }

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        // 第一步:获取openId接口响应
        String accessToken = userRequest.getAccessToken().getTokenValue();
        String openIdUrl = userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri() + "?access_token={accessToken}";
        String result = getRestTemplate().getForObject(openIdUrl, String.class, accessToken);
        // 提取openId
        String openId = result.substring(result.lastIndexOf(":\"") + 2, result.indexOf("\"}"));

        // 第二步:获取用户信息
        String appId = userRequest.getClientRegistration().getClientId();
        QQUserInfo qqUserInfo = getRestTemplate().getForObject(QQ_URL_GET_USER_INFO, QQUserInfo.class, appId, openId, accessToken);
        // 为用户信息类补充openId
        if (qqUserInfo != null) {
            qqUserInfo.setOpenId(openId);
        }
        return qqUserInfo;
    }
}

首先获取OpenId,再通过OpenId等参数获取用户信息,最终组装成QQUserInfo对象。

6. 多个OAuth2服务提供商并存

前面我们通过自定义实现QQOAuth2AccessTokenResponseClient和QQOAuth2UserService来支持QQ登录,但如果直接使用它们代替默认的NimbusAuthorizationCodeTokenResponseClient和DefaultOAuth2UserService,将会导致Github等标准OAuth2服务无法正常使用。为了允许多个OAuth服务并存,我们还可以使用组合模式进行设计:

6.1 提供CompositeOAuth2AccessTokenResponseClient
/**
 * OAuth2AccessTokenResponseClient的组合类,使用了Composite Pattern(组合模式)
 * 除了同时支持GOOGLE,OKTA,GITHUB,FACEBOOK之外,可能还需要同时支持QQ、微信等多种认证服务
 * 根据registrationId选择相应的OAuth2AccessTokenResponseClient
 */
public class CompositeOAuth2AccessTokenResponseClient implements OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> {

    private Map<String, OAuth2AccessTokenResponseClient> clients;

    private static final String DefaultClientKey = "default_key";

    public CompositeOAuth2AccessTokenResponseClient() {
        this.clients = new HashMap();
        // spring-security-oauth2-client默认的OAuth2AccessTokenResponseClient实现类是NimbusAuthorizationCodeTokenResponseClient
        // 将其预置到组合类CompositeOAuth2AccessTokenResponseClient中,从而默认支持GOOGLE,OKTA,GITHUB,FACEBOOK
        this.clients.put(DefaultClientKey, new NimbusAuthorizationCodeTokenResponseClient());
    }

    @Override
    public OAuth2AccessTokenResponse getTokenResponse(OAuth2AuthorizationCodeGrantRequest authorizationGrantRequest)
            throws OAuth2AuthenticationException {
        ClientRegistration clientRegistration = authorizationGrantRequest.getClientRegistration();

        OAuth2AccessTokenResponseClient client = clients.get(clientRegistration.getRegistrationId());
        if (client == null) {
            client = clients.get(DefaultClientKey);
        }

        return client.getTokenResponse(authorizationGrantRequest);
    }


    public Map<String, OAuth2AccessTokenResponseClient> getOAuth2AccessTokenResponseClients() {
        return clients;
    }
}
6.2 提供CompositeOAuth2UserService
/**
 * OAuth2AccessTokenResponseClient的组合类,使用了Composite Pattern(组合模式)
 * 除了同时支持GOOGLE,OKTA,GITHUB,FACEBOOK之外,可能还需要同时支持QQ、微信等多种认证服务
 * 根据registrationId选择相应的OAuth2AccessTokenResponseClient
 */
public class CompositeOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
    private Map<String, OAuth2UserService> userServices;

    private static final String DefaultUserServiceKey = "default_key";

    public CompositeOAuth2UserService() {
        this.userServices = new HashMap();
        // DefaultOAuth2UserService是默认处理OAuth2协议获取用户逻辑的OAuth2UserService实现类
        // 将其预置到组合类CompositeOAuth2UserService中,从而默认支持GOOGLE,OKTA,GITHUB,FACEBOOK
        this.userServices.put(DefaultUserServiceKey, new DefaultOAuth2UserService());
    }

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        ClientRegistration clientRegistration = userRequest.getClientRegistration();

        OAuth2UserService service = userServices.get(clientRegistration.getRegistrationId());
        if (service == null) {
            service = userServices.get(DefaultUserServiceKey);
        }

        return service.loadUser(userRequest);
    }

    public Map<String, OAuth2UserService> getUserServices() {
        return userServices;
    }
}

7. 配置Spring Security

从Spring Security 5.0开始,在HttpSecurity中提供了OAuth2Login用于配置OAuth2客户端策略:

http.OAuth2Login()

完整配置如下:

@EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    public static final String QQRegistrationId = "qq";
    public static final String WeChatRegistrationId = "wechat";

    public static final String LoginPagePath = "/login/oauth2";

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers(LoginPagePath).permitAll()
                .anyRequest()
                .authenticated();
        http.oauth2Login()
                // 使用CompositeOAuth2AccessTokenResponseClient
                .tokenEndpoint().accessTokenResponseClient(this.accessTokenResponseClient())
                .and()
            .userInfoEndpoint()
                .customUserType(QQUserInfo.class, QQRegistrationId)
                // 使用CompositeOAuth2UserService
                .userService(oauth2UserService())
                // 可选,要保证与redirect-uri-template匹配
                .and()
            .redirectionEndpoint().baseUri("/register/social/*");

        //自定义登录页
        http.oauth2Login().loginPage(LoginPagePath);
    }

    private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient() {
        CompositeOAuth2AccessTokenResponseClient client = new CompositeOAuth2AccessTokenResponseClient();
        // 加入QQ自定义QQOAuth2AccessTokenResponseClient
        client.getOAuth2AccessTokenResponseClients().put(QQRegistrationId, new QQOAuth2AccessTokenResponseClient());
        return client;
    }

    private OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {
        CompositeOAuth2UserService service = new CompositeOAuth2UserService();
        // 加入QQ自定义QQOAuth2UserService
        service.getUserServices().put(QQRegistrationId, new QQOAuth2UserService());
        return service;
    }
}

其中关于重定向端点( Redirection Endpoint)的配置是可选的,需要注意的是,当多种OAuth2服务提供商并存时,一定要保证baseUri、redirect-uri-template和
OAuth2注册的重定向地址三者相互匹配。

8. 工程配置文件

server:
  port: 8080

logging:
  level:
    root: INFO
    org.springframework.web: INFO
    org.springframework.security: DEBUG
    org.springframework.boot.autoconfigure: DEBUG

spring:
  security:
    oauth2:
      client:
        registration:
          github:
            client-id: {custom}
            client-secret: {custom}
            redirect-uri-template: "{baseUrl}/register/social/{registrationId}"
          qq:
            client-id: {custom appId}
            client-secret: {custom appKey}
            provider: qq
            client-name: QQ登录
            authorization-grant-type: authorization_code
            client-authentication-method: post
            scope: get_user_info,list_album,upload_pic,do_like
            redirect-uri-template: "{baseUrl}/register/social/{registrationId}"
        provider:
          qq:
            authorization-uri: https://graph.qq.com/oauth2.0/authorize
            token-uri: https://graph.qq.com/oauth2.0/token
            # 配置为QQ获取OpenId的Url
            user-info-uri: https://graph.qq.com/oauth2.0/me
            user-name-attribute: "nickname"

上面的配置同时支持了Github和QQ登录。

9. 自定登录页面配置

Spring Security的OAuth2功能通过DefaultLoginPageGeneratingFilter生成了一个默认的登录页面,同时也允许我们自定义。

9.1 定义login.html和index.html

我们使用thymeleaf模版,需要工程pom文件引入

<!--页面模版-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>

index.html为主页面,主要用于展示信息:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
    <head>
        <title>Spring Security - OAuth 2.0 Login</title>
        <meta charset="utf-8"/>
    </head>
    <body>
        <div style="float: right" th:fragment="logout" sec:authorize="isAuthenticated()">
            <div style="float:left">
                <span style="font-weight:bold">用户: </span><span sec:authentication="name"></span>
            </div>
            <div style="float:none">&nbsp;</div>
            <div style="float:right">
                <form action="#" th:action="@{/logout}" method="post">
                    <input type="submit" value="注销"/>
                </form>
            </div>
        </div>
        <h1>使用Spring Security OAuth 2.0 登录</h1>
        <div>
            恭喜您通过"<span style="font-weight:bold" th:text="${clientName}"></span>"
            登录成功
        </div>
    </body>
</html>

login.html则是我们自定义的登录页面:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>Spring Security - OAuth 2.0 Login</title>
        <meta charset="utf-8"/>
    </head>
    <body>
        <h1>自定义OAuth2登录页</h1>
        <div>
            <a href="/oauth2/authorization/github">Github登录</a>
            <a href="/oauth2/authorization/qq">QQ登录</a>
        </div>
    </body>
</html>
9.2 定义Controller映射
@Controller
public class MainController {

    @Autowired
    private OAuth2AuthorizedClientService authorizedClientService;

    @GetMapping("/")
    public String index(Model model, OAuth2AuthenticationToken authentication) {
        OAuth2AuthorizedClient authorizedClient = this.getAuthorizedClient(authentication);
        model.addAttribute("userName", authentication.getName());
        model.addAttribute("clientName", authorizedClient.getClientRegistration().getClientName());
        return "index";
    }

    @GetMapping("/login/oauth2")
    public String login() {
        return "login";
    }

    private OAuth2AuthorizedClient getAuthorizedClient(OAuth2AuthenticationToken authentication) {
        return this.authorizedClientService.loadAuthorizedClient(
                authentication.getAuthorizedClientRegistrationId(), authentication.getName());
    }
}
  • OAuth2AuthenticationToken可以获取当前用户信息,由Spring Security自动注入。
  • OAuth2AuthorizedClientService对象可以用来获取当前已经认证成功的OAuth2的客户端信息。
9.3 启用自定义登录页
http.oauth2Login()
    .loginPage("/login/oauth2")

10. 运行演示

  1. 运行client-social工程
  2. 浏览器地址栏输入:http://{ip|host}:{port}/,访问将得到如图2所示的页面:

image

注意在QQ互联平台注册网站回调域时填写的回调地址应配置在本地的hosts中,并改用该域名访问测试,直接通过localhost测试是无法成功的。
  1. 点击Github登录或者QQ登录按钮,按照提示进行登录操作,登录成功后会跳转到主页,效果如图3所示:

image

目录
相关文章
|
1月前
|
存储 安全 Java
Spring Boot整合Spring Security--学习笔记
Spring Boot整合Spring Security--学习笔记
52 0
|
10天前
|
安全 数据安全/隐私保护
Springboot+Spring security +jwt认证+动态授权
Springboot+Spring security +jwt认证+动态授权
|
20天前
|
存储 XML 缓存
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南(一)
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南
42 0
|
2天前
|
安全 Java 数据安全/隐私保护
使用Spring Security进行Java身份验证与授权
【4月更文挑战第16天】Spring Security是Java应用的安全框架,提供认证和授权解决方案。通过添加相关依赖到`pom.xml`,然后配置`SecurityConfig`,如设置用户认证信息和URL访问规则,可以实现应用的安全保护。认证流程包括请求拦截、身份验证、响应生成和访问控制。授权则涉及访问决策管理器,如基于角色的投票。Spring Security为开发者构建安全应用提供了全面且灵活的工具,涵盖OAuth2、CSRF保护等功能。
|
4天前
|
安全 前端开发 Java
Spring Security的授权管理器实现
Spring Security的授权管理器涉及用户登录后的token验证和权限检查。当用户携带token访问时,框架会验证token合法性及用户访问权限。自定义授权管理器`TokenAuthorizationManager`需实现`AuthorizationManager&lt;RequestAuthorizationContext&gt;`接口,处理校验逻辑,包括解析token、判断用户角色与访问资源的匹配。配置中在`SecurityConfig`注册该管理器以生效。测试表明,具有不同角色的用户可访问相应权限的资源,否则返回403错误。
114 4
|
4天前
|
安全 Java Spring
Spring Security的授权&鉴权
该文档介绍了授权和鉴权的概念,主要分为Web授权和方法授权。Web授权通过URL拦截进行,而方法授权利用注解控制权限,粒度更细但耦合度高。在Web授权的案例中,展示了如何在Spring Security中对特定URL设置角色控制。此外,还列举了Spring Security内置的控制操作方法,如permitAll()、denyAll()和hasRole()等,用于定义不同类型的用户访问权限。
105 7
|
9天前
|
安全 Java 数据库
Spring Security加密解密
Spring Security加密解密
|
10天前
|
Java 容器
SpringBoot使用配置注解开启自动配置功能&整合spring-boot-configuration-processor
SpringBoot使用配置注解开启自动配置功能&整合spring-boot-configuration-processor
12 0
|
29天前
|
安全 Java 数据库
Spring Security 权限管理详解与案例
Spring Security 是 Spring 框架中用于提供认证和访问控制的模块。它保护了成千上万的应用程序,使其免受未经授权的访问。本文将详细介绍 Spring Security 的权限管理功能,并通过一个实际案例来展示其用法。
62 0
|
29天前
|
存储 JavaScript 前端开发
Spring Boot + Vue: 实现文件导入导出功能
本文介绍了使用Spring Boot和Vue实现文件导入导出的步骤。在后端,Spring Boot通过`MultipartFile`接收上传文件,保存至服务器,并使用`ResponseEntity`提供文件下载。前端部分,Vue项目借助`axios`发送HTTP请求,实现文件选择、上传及下载功能。这种前后端分离的实现方式提高了应用的可维护性和可扩展性。
23 2

热门文章

最新文章