zoukankan      html  css  js  c++  java
  • springsecurity授权码模式项目实战

    一、前言

    1、客户端详情表oauth_client_details
    client_id:客户端ID
    resources_ids:可访问的资源服务器ID,不写则不校验
    client_secret:客户端密码,此处不能是明文,需要加密
    scope:客户端授权范围,不指定默认不校验范围
    authorized_grant_types:客户端授权类型,支持多个使用逗号分隔。(authorization_code,password,implicit,client_credentials,refresh_token)
    web_server_redirect_uri:服务器回调地址
    autoapprove:false:显示授权点击页,true:不显示自动授权
    
    2、默认提供的令牌访问端点
    /oauth/authorize:申请授权码code
    /oauth/token:获取令牌token
    /oauth/check_token:用于资源服务器请求端点来检查令牌是否有效
    /oauth/confirm_access:用户确认授权提交
    /oauth/error:授权服务错误信息
    /oauth/token_key:提供公有密钥的端点,使用JWT令牌时会使用
    
    3、授权码模式流程
    1、获取授权码code:
        浏览器输入:http://localhost:7001/auth/oauth/authorize?client_id=yyy&response_type=code
        用户登录
        用户授权:autoapprove为true则跳过授权
        携带code跳转到web_server_redirect_uri:https://www.xxx.com/?code=gEAogD
    2、获取token
        POST http://localhost:7001/auth/oauth/token
        Authorization: Basic 
            Username=client_id
            Password=client_secret
        Content-Type: application/x-www-form-urlencoded
            grant_type=authorization_code
            code=gEAogD
    

    二、代码

    1、安全配置类
    package com.wuxi.project.auth.config;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.core.userdetails.UserDetailsService;
    
    /**
     * 安全配置类
     */
    @EnableWebSecurity
    public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        private UserDetailsService userDetailsServiceImpl;
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(userDetailsServiceImpl);
        }
    
        @Bean // 密码模式需要使用
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }
    
    }
    
    2、认证服务器配置
    package com.wuxi.project.auth.config;
    
    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.core.userdetails.UserDetailsService;
    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 org.springframework.security.oauth2.provider.ClientDetailsService;
    import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
    import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
    import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
    import org.springframework.security.oauth2.provider.token.TokenStore;
    
    import javax.sql.DataSource;
    
    @Configuration
    @EnableAuthorizationServer // 标识为认证服务器
    public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    
        @Autowired
        private DataSource dataSource;
    
        @Bean
        public ClientDetailsService jdbcClientDetailsService() {
            return new JdbcClientDetailsService(dataSource);
        }
    
        /**
         * 配置被允许访问此认证服务器的客户端信息
         *
         * @param clients
         * @throws Exception
         */
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            // jdbc管理客户端
            clients.withClientDetails(jdbcClientDetailsService());
        }
    
        @Autowired
        private AuthenticationManager authenticationManager;
    
        @Autowired
        private UserDetailsService userDetailsServiceImpl;
    
        @Autowired
        private TokenStore tokenStore;
    
        @Bean
        public AuthorizationCodeServices jdbcAuthorizationCodeServices() {
            return new JdbcAuthorizationCodeServices(dataSource);
        }
    
        /**
         * 关于认证服务器端点配置
         *
         * @param endpoints
         * @throws Exception
         */
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            // 密码模式需要使用
            endpoints.authenticationManager(authenticationManager);
            // 刷新令牌需要使用
            endpoints.userDetailsService(userDetailsServiceImpl);
            // 令牌的管理方式
            endpoints.tokenStore(tokenStore);
            // 授权码管理策略
            endpoints.authorizationCodeServices(jdbcAuthorizationCodeServices());
        }
    
        /**
         * 令牌端点的安全配置
         *
         * @param security
         * @throws Exception
         */
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
            // 认证后可访问 /oauth/token_key , 默认拒绝访问
            security.tokenKeyAccess("permitAll()");
            // 认证后可访问 /oauth/check_token , 默认拒绝访问
            security.checkTokenAccess("isAuthenticated()");
        }
    
    }
    
    3、Token配置
    package com.wuxi.project.auth.config;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.oauth2.provider.token.TokenStore;
    import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
    
    import javax.sql.DataSource;
    
    @Configuration
    public class TokenConfig {
    
        @Autowired
        private DataSource dataSource;
    
        @Bean
        public TokenStore tokenStore() {
            // 在使用Jwt管理令牌时,出现了用户无法授权范围的问题,还未能解决
            return new JdbcTokenStore(dataSource);
        }
    
    }
    
    4、PasswordEncoder配置
    package com.wuxi.project.auth.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    
    @Configuration
    public class PasswordEncoderConfig {
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
    }
    
    5、UserDetailsService
    package com.wuxi.project.auth.service;
    
    import com.wuxi.project.auth.entities.JwtUser;
    import com.wuxi.project.common.entities.SysMenu;
    import com.wuxi.project.common.entities.SysUser;
    import com.wuxi.project.common.feign.IFeignSystemController;
    import org.apache.commons.collections.CollectionUtils;
    import org.apache.commons.lang.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.authentication.BadCredentialsException;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    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;
    
    @Service
    public class UserDetailsServiceImpl implements UserDetailsService {
    
        @Autowired
        private IFeignSystemController feignSystemController;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            // 1. 判断用户名是否为空
            if (StringUtils.isEmpty(username)) {
                throw new BadCredentialsException("用户名不能为空");
            }
            // 2. 通过用户名查询数据库中的用户信息
            SysUser sysUser = feignSystemController.findUserByUsername(username);
            if (sysUser == null) {
                throw new BadCredentialsException("用户名或密码错误");
            }
    
            // 3. 通过用户id去查询数据库的拥有的权限信息
            List<SysMenu> menuList = feignSystemController.findMenuListByUserId(sysUser.getId());
    
            // 4. 封装权限信息(权限标识符code)
            List<GrantedAuthority> authorities = null;
            if (CollectionUtils.isNotEmpty(menuList)) {
                authorities = new ArrayList<>();
                for (SysMenu menu : menuList) {
                    // 权限标识
                    String code = menu.getCode();
                    authorities.add(new SimpleGrantedAuthority(code));
                }
            }
    
            // 5. 构建UserDetails接口的实现类JwtUser对象
            JwtUser jwtUser = new JwtUser(sysUser.getId(), sysUser.getUsername(), sysUser.getPassword(),
                    sysUser.getNickName(), sysUser.getImageUrl(), sysUser.getMobile(), sysUser.getEmail(),
                    sysUser.getIsAccountNonExpired(), sysUser.getIsAccountNonLocked(),
                    sysUser.getIsCredentialsNonExpired(), sysUser.getIsEnabled(),
                    authorities);
    
            return jwtUser;
        }
    }
    
    6、UserDetails
    package com.wuxi.project.auth.entities;
    
    import com.alibaba.fastjson.annotation.JSONField;
    import lombok.Data;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.userdetails.UserDetails;
    
    import java.util.List;
    
    @Data
    public class JwtUser implements UserDetails {
    
        /**
         * 用户ID
         */
        private String uid;
    
        /**
         * 用户名
         */
        private String username;
    
        /**
         * 密码,加密存储, admin/1234
         */
        @JSONField(serialize = false) // 忽略转json
        private String password;
    
        /**
         * 昵称
         */
        private String nickName;
    
        /**
         * 头像url
         */
        private String imageUrl;
    
        /**
         * 注册手机号
         */
        private String mobile;
    
        /**
         * 注册邮箱
         */
        private String email;
    
        /**
         * 帐户是否过期(1 未过期,0已过期)
         * 1 true 0 false
         */
        @JSONField(serialize = false) // 忽略转json
        private boolean isAccountNonExpired; // 不要写小写 boolean
    
        /**
         * 帐户是否被锁定(1 未过期,0已过期)
         */
        @JSONField(serialize = false) // 忽略转json
        private boolean isAccountNonLocked;
    
        /**
         * 密码是否过期(1 未过期,0已过期)
         */
        @JSONField(serialize = false) // 忽略转json
        private boolean isCredentialsNonExpired;
    
        /**
         * 帐户是否可用(1 可用,0 删除用户)
         */
        @JSONField(serialize = false) // 忽略转json
        private boolean isEnabled;
    
        /**
         * 封装用户拥有的菜单权限标识
         */
        @JSONField(serialize = false) // 忽略转json
        private List<GrantedAuthority> authorities;
    
        //    isAccountNonExpired 是 Integer 类型接收,然后转 boolean
        public JwtUser(String uid, String username, String password,
                       String nickName, String imageUrl, String mobile, String email,
                       Integer isAccountNonExpired, Integer isAccountNonLocked,
                       Integer isCredentialsNonExpired, Integer isEnabled,
                       List<GrantedAuthority> authorities) {
            this.uid = uid;
            this.username = username;
            this.password = password;
            this.nickName = nickName;
            this.imageUrl = imageUrl;
            this.mobile = mobile;
            this.email = email;
            this.isAccountNonExpired = isAccountNonExpired == 1 ? true : false;
            this.isAccountNonLocked = isAccountNonLocked == 1 ? true : false;
            this.isCredentialsNonExpired = isCredentialsNonExpired == 1 ? true : false;
            this.isEnabled = isEnabled == 1 ? true : false;
            this.authorities = authorities;
        }
    
    }
    

    三、资源服务器

    1、Token配置
    package com.wuxi.project.auth.config;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.oauth2.provider.token.TokenStore;
    import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
    
    import javax.sql.DataSource;
    
    
    /**
     * @Auther: ll
     */
    @Configuration
    public class TokenConfig {
    
        @Autowired
        private DataSource dataSource;
    
        @Bean
        public TokenStore tokenStore() {
            return new JdbcTokenStore(dataSource);
        }
    
    }
    
    2、资源服务器配置
    package com.wuxi.project.auth.config;
    
    import org.springframework.beans.factory.annotation.Autowired;
    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.http.SessionCreationPolicy;
    import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
    import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
    import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
    import org.springframework.security.oauth2.provider.token.TokenStore;
    
    /**
     * 资源服务器相关配置类
     *
     * @Auther: ll
     */
    @Configuration
    @EnableResourceServer // 标识为资源服务器,请求服务中的资源,就要带着token过来,找不到token或token是无效访问不了资源
    @EnableGlobalMethodSecurity(prePostEnabled = true) // 开启方法级别权限控制
    public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    
        public static final String RESOURCE_ID = "system";
    
        @Autowired
        private TokenStore tokenStore;
    
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            // 当前资源服务器的资源id,认证服务会认证客户端有没有访问这个资源id的权限,有则可以访问当前服务
            resources.resourceId(RESOURCE_ID)
                    .tokenStore(tokenStore)
            ;
        }
    
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.sessionManagement() // 采用token进行管理身份,而没有采用session,所以不需要创建HttpSession
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                    .authorizeRequests() // 请求的授权配置
                    // 放行以 /api 开头的请求接口
                    .antMatchers("/api/**").permitAll()
                    // 所有请求都要有all范围权限
                    .antMatchers("/**").access("#oauth2.hasScope('all')")
                    // 其他请求都要通过身份认证
                    .anyRequest().authenticated();
        }
    
    }
    
  • 相关阅读:
    如何判断第一个节区头的RVA
    从可执行文件中删除.reloc节区
    动态规划(dynamic programming)
    Ubuntu18安装SQL server
    Ubuntu16.04突然断网
    [Toddler's Bottle]做题记录
    BUU | pwnable_orw
    BUU| 基础破解
    web.xml
    PKIX
  • 原文地址:https://www.cnblogs.com/linding/p/14994324.html
Copyright © 2011-2022 走看看