博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring Security OAuth2 SSO
阅读量:7282 次
发布时间:2019-06-30

本文共 15390 字,大约阅读时间需要 51 分钟。

通常公司肯定不止一个系统,每个系统都需要进行认证和权限控制,不可能每个每个系统都自己去写,这个时候需要把登录单独提出来

  1. 登录和授权是统一的
  2. 业务系统该怎么写还怎么写

最近学习了一下Spring Security,今天用Spring Security OAuth2简单写一个单点登录的示例

在此之前,需要对OAuth2有一点了解

这里有几篇文章可能会对你有帮助

《》

《》

《》

《》

《》

1. 服务器端配置

1.1. Maven依赖

4.0.0
com.cjs.example
cjs-oauth2-sso-auth-server
0.0.1-SNAPSHOT
jar
cjs-oauth2-sso-auth-server
org.springframework.boot
spring-boot-starter-parent
2.0.3.RELEASE
UTF-8
UTF-8
1.8
org.springframework.boot
spring-boot-starter-security
org.springframework.security.oauth
spring-security-oauth2
2.3.3.RELEASE
org.springframework.boot
spring-boot-starter-thymeleaf
org.thymeleaf.extras
thymeleaf-extras-springsecurity4
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-jdbc
mysql
mysql-connector-java
5.1.46
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.springframework.security
spring-security-test
test
org.springframework.boot
spring-boot-maven-plugin

1.2. 配置授权服务器

package com.cjs.example.config;import org.springframework.context.annotation.Configuration;import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;import javax.annotation.Resource;import javax.sql.DataSource;@Configuration@EnableAuthorizationServerpublic class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {    @Resource    private DataSource dataSource;    /**     * 配置授权服务器的安全,意味着实际上是/oauth/token端点。     * /oauth/authorize端点也应该是安全的     * 默认的设置覆盖到了绝大多数需求,所以一般情况下你不需要做任何事情。     */    @Override    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {        super.configure(security);    }    /**     * 配置ClientDetailsService     * 注意,除非你在下面的configure(AuthorizationServerEndpointsConfigurer)中指定了一个AuthenticationManager,否则密码授权方式不可用。     * 至少配置一个client,否则服务器将不会启动。     */    @Override    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {        clients.jdbc(dataSource);    }    /**     * 该方法是用来配置Authorization Server endpoints的一些非安全特性的,比如token存储、token自定义、授权类型等等的     * 默认情况下,你不需要做任何事情,除非你需要密码授权,那么在这种情况下你需要提供一个AuthenticationManager     */    @Override    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {        super.configure(endpoints);    }}

说明:这里授权服务器我主要是配置了注册客户端,客户端可以从内存中或者数据库中加载,这里我从数据库中加载,因为这样感觉更真实一点儿。

查看JdbcClientDetailsService源码我们不难看出其表结构。(PS:也可以自定义,就像UserDetailsService那样)

这里,我准备的SQL脚本如下:

CREATE TABLE oauth_client_details (    client_id VARCHAR(256) PRIMARY KEY,    resource_ids VARCHAR(256),    client_secret VARCHAR(256),    scope VARCHAR(256),    authorized_grant_types VARCHAR(256),    web_server_redirect_uri VARCHAR(256),    authorities VARCHAR(256),    access_token_validity INTEGER,    refresh_token_validity INTEGER,    additional_information VARCHAR(4096),    autoapprove VARCHAR(256));INSERT INTO oauth_client_details (client_id, client_secret, scope, authorized_grant_types, web_server_redirect_uri, autoapprove)VALUES ('MemberSystem', '$2a$10$dYRcFip80f0jIKGzRGulFelK12036xWQKgajanfxT65QB4htsEXNK', 'user_info', 'authorization_code', 'http://localhost:8081/login', 'user_info');INSERT INTO oauth_client_details (client_id, client_secret, scope, authorized_grant_types, web_server_redirect_uri, autoapprove)VALUES ('CouponSystem', '$2a$10$dYRcFip80f0jIKGzRGulFelK12036xWQKgajanfxT65QB4htsEXNK', 'user_info', 'authorization_code', 'http://localhost:8082/login', 'user_info');

这里注册了两个客户端,分别是MemberSystem和CouponSystem。

1.3. 配置WebSecurity

package com.cjs.example.config;import com.cjs.example.support.MyUserDetailsService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.security.authentication.AuthenticationManager;import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.builders.WebSecurity;import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.security.crypto.password.PasswordEncoder;@Configuration@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {    @Autowired    private MyUserDetailsService myUserDetailsService;    @Override    protected void configure(HttpSecurity http) throws Exception {        http.authorizeRequests()                .antMatchers("/oauth/**","/login/**", "/logout").permitAll()                .anyRequest().authenticated()   // 其他地址的访问均需验证权限                .and()                .formLogin()                .loginPage("/login")                .and()                .logout().logoutSuccessUrl("/");    }    @Override    public void configure(WebSecurity web) throws Exception {        web.ignoring().antMatchers("/assets/**");    }    @Override    protected void configure(AuthenticationManagerBuilder auth) throws Exception {        auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder());    }    @Bean    @Override    public AuthenticationManager authenticationManager() throws Exception {        return super.authenticationManager();    }    @Bean    public PasswordEncoder passwordEncoder() {        return new BCryptPasswordEncoder();    }}

说明:

  1. 这里,主要配置了UserDetailsService
package com.cjs.example.support;import com.cjs.example.domain.SysPermission;import com.cjs.example.domain.SysRole;import com.cjs.example.domain.SysUser;import com.cjs.example.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.core.authority.SimpleGrantedAuthority;import org.springframework.security.core.userdetails.User;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.core.userdetails.UsernameNotFoundException;import org.springframework.stereotype.Service;import java.util.ArrayList;import java.util.List;@Servicepublic class MyUserDetailsService implements UserDetailsService {    @Autowired    private UserService userService;    /**     * 授权的时候是对角色授权,而认证的时候应该基于资源,而不是角色,因为资源是不变的,而用户的角色是会变的     */    @Override    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {        SysUser sysUser = userService.getUserByName(username);        if (null == sysUser) {            throw new UsernameNotFoundException(username);        }        List
authorities = new ArrayList<>(); for (SysRole role : sysUser.getRoleList()) { for (SysPermission permission : role.getPermissionList()) { authorities.add(new SimpleGrantedAuthority(permission.getCode())); } } return new User(sysUser.getUsername(), sysUser.getPassword(), authorities); }}

1.4. 新建登录页面

package com.cjs.example.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;@Controllerpublic class LoginController {    @RequestMapping("/login")    public String login() {        return "login";    }    @GetMapping("/index")    public String index() {        return "index";    }}

1.5. application.yml

server:  port: 8080spring:  datasource:    url: jdbc:mysql://10.123.52.189:3306/oh_coupon    username: devdb    password: d^V$0Fu!/6-

2. 客户端配置

2.1. Maven依赖

4.0.0
com.example
cjs-oauth2-sso-ui
0.0.1-SNAPSHOT
jar
cjs-oauth2-sso-ui
org.springframework.boot
spring-boot-starter-parent
2.0.3.RELEASE
UTF-8
UTF-8
1.8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-security
org.springframework.boot
spring-boot-starter-thymeleaf
org.thymeleaf.extras
thymeleaf-extras-springsecurity4
org.springframework.security.oauth
spring-security-oauth2
2.3.3.RELEASE
org.springframework.security.oauth.boot
spring-security-oauth2-autoconfigure
2.0.1.RELEASE
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-maven-plugin

2.2. WebSecurity配置

package com.cjs.example.config;import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;@EnableOAuth2Sso@Configuration@EnableGlobalMethodSecurity(prePostEnabled = true)public class UiSecurityConfig extends WebSecurityConfigurerAdapter {    @Override    public void configure(HttpSecurity http) throws Exception {        http.antMatcher("/**")                .authorizeRequests()                .antMatchers("/", "/login**").permitAll()                .anyRequest()                .authenticated();    }}

说明:

  这里最重要的是应用了@EnableOAuth2Sso注解

  Spring Boot 1.x 版本和 2.x 版本在OAuth2这一块的差异还是比较大的,在Spring Boot 2.x 中没有@EnableOAuth2Sso这个注解,所以我引用了spring-security-oauth2-autoconfigure

2.3. 定义一个简单的控制器

package com.cjs.example.controller;import com.cjs.example.domain.Member;import org.springframework.security.access.prepost.PreAuthorize;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.servlet.ModelAndView;import java.util.ArrayList;import java.util.List;@Controller@RequestMapping("/member")public class MemberController {    /**     * 会员列表页面     */    @RequestMapping("/list")    public ModelAndView list() {        ModelAndView modelAndView = new ModelAndView("member/list");        return modelAndView;    }    /**     * 导出     */    @PreAuthorize("hasAuthority('memberExport')")    @ResponseBody    @RequestMapping("/export")    public List
export() { Member member = new Member(); member.setName("苏九儿"); member.setCode("1000"); member.setMobile("13112345678"); member.setGender(1); Member member1 = new Member(); member1.setName("郭双"); member1.setCode("1001"); member1.setMobile("15812346723"); member1.setGender(1); List
list = new ArrayList<>(); list.add(member); list.add(member1); return list; } /** * 详情 */ @PreAuthorize("hasAuthority('memberDetail')") @RequestMapping("/detail") public ModelAndView detail() { return new ModelAndView(" member/detail"); }}

2.4. application.yml

server:  port: 8081  servlet:    session:      cookie:        name: UISESSIONMEMBERsecurity:  oauth2:    client:      client-id: MemberSystem      client-secret: 12345      access-token-uri: http://localhost:8080/oauth/token      user-authorization-uri: http://localhost:8080/oauth/authorize    resource:      user-info-uri: http://localhost:8080/user/melogging:  level:    root: debugspring:  thymeleaf:    cache: false

说明:

  1. 这里需要注意的是不要忘记设置cookie-name,不然会有一些莫名其妙的问题,比如“User must be authenticated with Spring Security before authorization can be completed”

3. 运行效果

在这个例子中,会员系统(localhost:8081)和营销系统(localhost:8082)是两个系统

可以看到,当我们登录会员系统以后,再进营销系统就不需要登录了。

3.1. 遗留问题

  1. 退出
  2. 记住我

3.2. 工程结构

https://github.com/chengjiansheng/cjs-oauth2-example.git

3.3. 参考

转载地址:http://lykjm.baihongyu.com/

你可能感兴趣的文章
DEDECMS后台三级子栏目权限添加修复,频道管理员权限分配问题解决
查看>>
判断App是否在前台
查看>>
可管理二层,三层,四层交换机的区别
查看>>
ARM中断PC返回值分析
查看>>
hyper-v故障转移群集之3、连接iscsi存储
查看>>
iPad app应用开发系列文章之三 -- iOS的多核编程和内存管理
查看>>
一件发生在北大校园里的事
查看>>
Activity四种启动模式
查看>>
java自定义注解日志
查看>>
勒索病毒瞄准手机,如何预防?
查看>>
批量文件改名案例实战
查看>>
一分钟解决上网速度慢的问题
查看>>
屏幕亮度变暗解决方法
查看>>
动软社会化分享社区系统V1.3版发布
查看>>
linux下lvs搭建负载均衡集群
查看>>
程序生命周期(ActivityLifeCycle)
查看>>
实战:搭建dns服务
查看>>
Delphi动态结构体数组传递值
查看>>
CSS DIV中Visibility和Display属性的区别
查看>>
Vue.js-组件
查看>>