Spring Security OAuth2 JWT实现SSO

 转载务必说明出处:https://blog.csdn.net/LiaoHongHB/article/details/84032850

     在这里我们以一台服务器和2台客户端做测试,在客户端1进行登陆之后,访问客服端2的时候不需要进行登陆就可访问(类似于淘宝登陆之后,在淘宝网点击天猫链接会发现天猫网已经是登陆状态)

       jwt-server代码:

          资源服务器:

@Configuration
@EnableWebSecurity
public class SsoSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

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

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .and()
                .authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .csrf().disable();
    }
}

          认证服务器:

@Configuration
@EnableAuthorizationServer
public class SsoAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("client1")
                .secret("clientsecret1")
                //支持的授权模式(数组类型)
                .authorizedGrantTypes("authorization_code", "refresh_token")
                .scopes("all")
                .and()
                .withClient("client2")
                .secret("clientsecret2")
                .authorizedGrantTypes("authorization_code", "refresh_token")
                .scopes("all");
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(jwtTokenStore()).accessTokenConverter(jwtAccessTokenConverter());
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.tokenKeyAccess("isAuthenticated()");
    }

    @Bean
    public TokenStore jwtTokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        //JWT签名
        converter.setSigningKey("jwt");
        return converter;
    }
}

UserDetailService:

@Component
public class SsoUserDetailService implements UserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return new User(username,passwordEncoder.encode("123456"),
                AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ADMIN"));
    }
}

Controller:

@RestController
@SessionAttributes("authorizationRequest")
public class SsoApprovalEndpointController {

    @RequestMapping({"/oauth/confirm_access"})
    public ModelAndView getAccessConfirmation(Map<String, Object> model, HttpServletRequest request) throws Exception {
        String template = this.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<String, Object> model, HttpServletRequest request) {
        String template = TEMPLATE;
        if (!model.containsKey("scopes") && request.getAttribute("scopes") == null) {
            template = template.replace("%scopes%", "").replace("%denial%", DENIAL);
        } else {
            template = template.replace("%scopes%", this.createScopes(model, request)).replace("%denial%", "");
        }

        if (!model.containsKey("_csrf") && request.getAttribute("_csrf") == null) {
            template = template.replace("%csrf%", "");
        } else {
            template = template.replace("%csrf%", CSRF);
        }

        return template;
    }

    private CharSequence createScopes(Map<String, Object> model, HttpServletRequest request) {
        StringBuilder builder = new StringBuilder("<ul>");
        Map<String, String> scopes = (Map)((Map)(model.containsKey("scopes") ? model.get("scopes") : request.getAttribute("scopes")));
        Iterator var5 = scopes.keySet().iterator();

        while(var5.hasNext()) {
            String scope = (String)var5.next();
            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("</ul>");
        return builder.toString();
    }

    private static String CSRF = "<input type='hidden' name='${_csrf.parameterName}' value='${_csrf.token}' />";
    private static String DENIAL = "<form id='denialForm' name='denialForm' action='${path}/oauth/authorize' method='post'><input name='user_oauth_approval' value='false' type='hidden'/>%csrf%<label><input name='deny' value='Deny' type='submit'/></label></form>";
    private static String TEMPLATE = "<html>" +
            "<body>" +
            "<div style='display:none;'>"+
            "<h1>OAuth Approval</h1>" +
            "<p>Do you authorize '${authorizationRequest.clientId}' to access your protected resources?</p>" +
            " <form id='confirmationForm' name='confirmationForm' action='${path}/oauth/authorize' method='post'>" +
            "<input name='user_oauth_approval' value='true' type='hidden'/>%csrf%%scopes%" +
            "<label>" + "<input name='authorize' value='Authorize' type='submit'/></label>" +
            "</form>" +
            "%denial%" +
            "</div>"+
            "<script>document.getElementById('confirmationForm').submit()</script>"+
            "</body>" +
            "</html>";
    private static String SCOPE = "<li><div class='form-group'>%scope%: <input type='radio' name='%key%' value='true'%approved%>Approve</input> <input type='radio' name='%key%' value='false'%denied%>Deny</input></div></li>";
}

View.class:

public class SsoSpelView implements View {

    private final String template;

    private final String prefix;

    private final SpelExpressionParser parser = new SpelExpressionParser();

    private final StandardEvaluationContext context = new StandardEvaluationContext();

    private PropertyPlaceholderHelper.PlaceholderResolver resolver;

    public SsoSpelView(String template) {
        this.template = template;
        this.prefix = new RandomValueStringGenerator().generate() + "{";
        this.context.addPropertyAccessor(new MapAccessor());
        this.resolver = new PropertyPlaceholderHelper.PlaceholderResolver() {
            public String resolvePlaceholder(String name) {
                Expression expression = parser.parseExpression(name);
                Object value = expression.getValue(context);
                return value == null ? null : value.toString();
            }
        };
    }

    public String getContentType() {
        return "text/html";
    }

    public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        Map<String, Object> map = new HashMap<String, Object>(model);
        String path = ServletUriComponentsBuilder.fromContextPath(request).build()
                .getPath();
        map.put("path", (Object) path==null ? "" : path);
        context.setRootObject(map);
        String maskedTemplate = template.replace("${", prefix);
        PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper(prefix, "}");
        String result = helper.replacePlaceholders(maskedTemplate, resolver);
        result = result.replace(prefix, "${");
        response.setContentType(getContentType());
        response.getWriter().append(result);
    }
}

client1代码:

application.yml:

server:
  port: 5001
  servlet:
    context-path: /client1

security:
  oauth2:
    client:
      client-id: client1
      client-secret: clientsecret1
      user-authorization-uri:  http://127.0.0.1:5000/server/oauth/authorize
      access-token-uri:  http://127.0.0.1:5000/server/oauth/token
    resource:
      jwt:
        key-uri:  http://127.0.0.1:5000/server/oauth/token_key

index.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>sso client2</title>
</head>
<body>
      <h1>SSO CLIENT2 DEMO</h1><br>
      <a href="http://127.0.0.1:5002/client2/index.jsp">访问client1</a>
</body>
</html>

client2代码:

application.yml:

server:
  port: 5002
  servlet:
    context-path: /client2

security:
  oauth2:
    client:
      client-id: client2
      client-secret: clientsecret2
      user-authorization-uri:  http://127.0.0.1:5000/server/oauth/authorize
      access-token-uri:  http://127.0.0.1:5000/server/oauth/token
    resource:
      jwt:
        key-uri:  http://127.0.0.1:5000/server/oauth/token_key

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>sso client2</title>
</head>
<body>
      <h1>SSO CLIENT2 DEMO</h1><br>
      <a href="http://127.0.0.1:5001/client1/index.jsp">访问client1</a>
</body>
</html>

测试:先运行server,然后客户端。

地址栏输入:localhost:5001/client1/index.jsp  --->回车键

然后会跳转到:localhost:5000/sever/login

输入用户名和密码:这里为了方便测试,用户名可以随便输,密码必须为123456

点击Login按钮即返回到client1/index.jsp页面

这个时候点击“访问client2”,在不需要登陆的情况下就可以访问到client2/index.jsp页面(即完成单点登陆类似于淘宝登陆天猫)

 在client2/index.jsp中访问client1也是不需要登陆就可访问了。

猜你喜欢

转载自blog.csdn.net/LiaoHongHB/article/details/84032850