Spring Security基于JWT实现SSO单点登录详解

网友投稿 286 2022-12-28


Spring Security基于JWT实现SSO单点登录详解

SSO :同一个帐号在同一个公司不同系统上登陆

使用SpringSecurity实现类似于SSO登陆系统是十分简单的 下面我就搭建一个DEMO

首先来看看目录的结构

其中sso-demo是父工程项目 sso-client 、sso-client2分别对应2个资源服务器,sso-server是认证服务器

引入的pom文件

sso-demo

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

study.security.sso

sso-demo

1.0.0-SNAPSHOT

sso-server

sso-client

sso-client2

pom

io.spring.platform

platform-bom

Brussels-SR4

pom

import

org.springframework.cloud

spring-cloud-dependencies

Dalston.SR2

pom

import

org.apache.maven.plugins

maven-compiler-plugin

2.3.2

1.8

1.8

UTF-8

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

study.security.sso

sso-demo

1.0.0-SNAPSHOT

sso-server

sso-client

sso-client2

pom

io.spring.platform

platform-bom

Brussels-SR4

pom

import

org.springframework.cloud

spring-cloud-dependencies

Dalston.SR2

pom

import

org.apache.maven.plugins

maven-compiler-plugin

2.3.2

1.8

1.8

UTF-8

sso-server

xmlns:xsi="http://w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

sso-demo

study.security.sso

1.0.0-SNAPSHOT

4.0.0

sso-server

org.springframework.boot

spring-boot-starter-security

org.springframework.boot

spring-boot-starter-web

org.springframework.security.oauth

spring-security-oauth2

org.springframework.security

spring-security-jwt

xmlns:xsi="http://w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

sso-demo

study.security.sso

1.0.0-SNAPSHOT

4.0.0

sso-server

org.springframework.boot

spring-boot-starter-security

org.springframework.boot

spring-boot-starter-web

org.springframework.security.oauth

spring-security-oauth2

org.springframework.security

spring-security-jwt

sso-client与sso-client2 pom 中的 是一样的

1.sso-server

现在开始搭建认证服务器

认证服务器的目录结构如下

/**

* 认证服务器配置

* Created by ZhuPengWei on 2018/1/11.

*/

@Configuration

@EnableAuthorizationServer

public class SsoAuthenticationServerConfig extends AuthorizationServerConfigurerAdapter {

@Override

public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

clients.inMemory()

.withClient("client1")

.secret("client1")

.authorizedGrantTypes("authorization_code", "refresh_token")

.scopes("all")

.and()

.withClient("client2")

.secret("client2")

.authorizedGrantTypes("authorization_code", "refresh_token")

.scopes("all");

}

@Override

public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

endpoints.tokenStore(jwtTokenStore()).accessTokenConverter(jwtAccessTokenConverter());

}

/**

* 认证服务器的安全配置

*

* @param security

* @throws Exception

*/

@Override

public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {

// 要访问认证服务器tokenKey的时候需要经过身份认证

security.tokenKeyAccess("isAuthenticated()");

}

@Bean

public TokenStore jwtTokenStore() {

return new JwtTokenStore(jwtAccessTokenConverter());

}

@Bean

public JwtAccessTokenConverter jwtAccessTokenConverter() {

JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();

// 保证JWT安全的唯一方式

jwtAccessTokenConverter.setSigningKey("ZPW");

return jwtAccessTokenConverter;

}

}

/**

* 自定义用户登陆逻辑配置

* Created by ZhuPengWei on 2018/1/13.

*/

@Configuration

public class SsoSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired

private UserDetailsService userDetailsService;

/**

* 加密解密逻辑

*/

@Bean

public PasswordEncoder passwordEncoder() {

return new BCryptPasswordEncoder();

}

@Override

protected void configure(HttpSecurity http) throws Exception {

// 改成表单登陆的方式 所有请求都需要认证

http.formLogin().and().authorizeRequests().anyRequest().authenticated();

}

@Override

protected void configure(AuthenticationManagerBuilder auth) throws Exception {

// 用自己的登陆逻辑以及加密器

auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());

}

}

/**

* 自定义用户登陆

* Created by ZhuPengWei on 2018/1/13.

*/

@Component

public class SsoUserDetailsService implements UserDetailsService {

@Autowired

private PasswordEncoder passwordEncoder;

@Override

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

return new User(username,

passwordEncoder.encode("123456"),

AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));

}

}

其中SsoApprovalEndPoint与SsoSpelView目的是去掉登陆之后授权的效果

注解 @FrameworkEndpoint

与@RestController注解相类似

如果声明和@FrameworkEndpoint一模一样的@RequestMapping

Spring框架处理的时候会优先处理@RestController里面的

/**

* 自定义认证逻辑

* Created by ZhuPengWei on 2018/1/13.

*/

@RestController

@SessionAttributes("authorizationRequest")

public class SsoApprovalEndpoint {

@RequestMapping("/oauth/confirm_access")

public ModelAndView getAccessConfirmation(Map model, HttpServletRequest request) throws Exception {

String template = createTemplate(model, request);

if (request.getAttribute("_csrf") != null) {

model.put("_csrf", request.getAttribute("_csrf"));

}

return new ModelAndView(new SsoSpelView(template), model);

}

protected String createTemplate(Map model, HttpServletRequest request) {

String template = TEMPLATE;

if (model.containsKey("scopes") || request.getAttribute("scopes") != null) {

template = template.replace("%scopes%", createScopes(model, request)).replace("%denial%", "");

} else {

template = template.replace("%scopes%", "").replace("%denial%", DENIAL);

}

if (model.containsKey("_csrf") || request.getAttribute("_csrf") != null) {

template = template.replace("%csrf%", CSRF);

} else {

template = template.replace("%csrf%", "");

}

return template;

}

private CharSequence createScopes(Map model, HttpServletRequest request) {

StringBuilder builder = new StringBuilder("

@SuppressWarnings("unchecked")

Map scopes = (Map) (model.containsKey("scopes") ? model.get("scopes") : request

.getAttribute("scopes"));

for (String scope : scopes.keySet()) {

String approved = "true".equals(scopes.get(scope)) ? " checked" : "";

String denied = !"true".equals(scopes.get(scope)) ? " checked" : "";

String value = SCOPE.replace("%scope%", scope).replace("%key%", scope).replace("%approved%", approved)

.replace("%denied%", denied);

builder.append(value);

}

builder.append("

return builder.toString();

}

private static String CSRF = "";

private static String DENIAL = "

// 对源代码进行处理 隐藏授权页面,并且使他自动提交

private static String TEMPLATE = "

+ "

Do you authorize '${authorizationRequest.clientId}' to access your protected resources?

+ "

+ "%denial%

private static String SCOPE = "

+ " value='true'%approved%>Approve Deny

}

SsoSpelView 与 原来SpelView 是一样的 只不过原来SpelView 不是public的类

application.properties

server.port=9999

server.context-path=/server

2.sso-client

相对于认证服务器 资源服务器demo的配置就十分简单了

/**

* Created by ZhuPengWei on 2018/1/11.

*/

@SpringBootApplication

@RestController

@EnableOAuth2Sso

public class SsoClient1Application {

@GetMapping("/user")

public Authentication user(Authentication user) {

return user;

}

public static void main(String[] args) {

SpringApplication.run(SsoClient1Application.class, args);

}

}

访问client2

application.properties

security.oauth2.client.client-id=client1

security.oauth2.client.client-secret=client1

#需要认证时候跳转的地址

security.oauth2.client.user-authorization-uri=http://127.0.0.1:9999/server/oauth/authorize

#请求令牌地址

security.oauth2.client.access-token-uri=http://127.0.0.1:9999/server/oauth/token

#解析

security.oauth2.resource.jwt.key-uri=http://127.0.0.1:9999/server/oauth/token_key

#sso

server.port=8080

server.context-path=/client1

3.sso-client2

资源服务器1和资源服务器2的目录结构是一样的,改了相关的参数

/**

* Created by ZhuPengWei on 2018/1/11.

*/

@SpringBootApplication

@RestController

@EnableOAuth2Sso

public class SsoClient2Application {

@GetMapping("/user")

public Authentication user(Authentication user) {

return user;

}

public static void main(String[] args) {

SpringApplication.run(SsoClient2Application.class, args);

}

}

访问client1

security.oauth2.client.client-id=client2

security.oauth2.client.client-secret=client2

#需要认证时候跳转的地址

security.oauth2.client.user-authorization-uri=http://127.0.0.1:9999/server/oauth/authorize

#请求令牌地址

security.oauth2.client.access-token-uri=http://127.0.0.1:9999/server/oauth/token

#解析

security.oauth2.resource.jwt.key-uri=http://127.0.0.1:9999/server/oauth/token_key

#sso

server.port=8060

server.context-path=/client2

好了 基于JWT实现SSO单点登录的DEMO以及搭建完成了 下面来看看页面的效果

在初次访问的时候

图1

登陆成功之后

图2

图3

注意

写SsoApprovalEndPoint与SsoSpelView目的是去掉登陆之后授权的效果如果不写这2个类

在初次访问的登陆成功之后是有一步授权的操作的

比如说图1操作成功之后

点击Authorize才会跳转到图2


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Spring Boot配置过滤器的2种方式示例
下一篇:包含小型超市系统接口设计的词条
相关文章

 发表评论

暂时没有评论,来抢沙发吧~