SpringSecurity权限控制-基础配置

本文有oAuth2授权流程、授权服务配置、资源服务配置。

Security认证流程:

1、请求先到持久的SecurityContextPersistenceFilter,这个filter会先从SecurityContextRepository仓库中获取SecurityContext,若找到,则将SecurityContext设置到SecurityContextHolder中,那么在后面的代码中,直接从SecurityContextHolder中获取SecurityContext。
2、若没找到,则请求到UsernamepasswordAuthenticationFilter,这个filter将用户名与密码封装成一个Token,并调用AuthenticationManager做认证。
3、AuthenticationManager调用AuthenticationProvider做认证,AuthenticationProvider调用UserDetailsService加载数据库中的数据得到UserDetails对象。
4、AuthenticationProvider调用PasswordEncoder对比传入的密码和数据库中获取的密码是否一致。
5、校验成功后,将用户信息封装成UsernamePasswordAuthenticationToken对象,设置给SecurityContext,SecurityContext设置给SecurityContextHolder。
6、持久的SecurityContextPersistenceFilter从SecurityContextHolder中取出SecurityContext,存储到SecurityContextRepository中,并删除SecurityContextHolder中的数据。
7、请求再次到达时,执行第一步。

在这里插入图片描述

oAuth2授权流程:

1、授权服务执行流程

(1)获取授权码执行流程

1、执行登录时,会走Security认证流程,调用UserDetailsServer加载认证信息和权限列表;
2、获取授权码时,根据传入的ClientId,调用JdbcClientDetailsService获取数据库(表:oauth_client_details)中客户端的配置详情;
3、使用JdbcAuthorizationCodeServices生成授权码code,存储到数据库(表:oauth_code);
4、携带授权码,重定向到指定的url(表oauth_client_details中的web_server_redirect_uri字段指定的值);

(2)获取Token执行流程

1、请求携带授权码和一些参数到达,后台会先校验参数和授权码;
2、验证通过,根据用户的认证信息、授权信息、客户端详情信息生成一个Token,存储到内存;
3、返回Token给客户端;
2、资源服务授权流程
1、携带Token访问资源服务器;
2、资源服务器会携带Token到认证服务器发起请求验证Token;
3、检查Token,获取到Token背后的认证信息、权限信息,校验Token是否包含resourceId;
4、根据Token的权限列表,走Security方法授权;

授权服务配置

依赖:
<!--oauth2的包,包含了security包-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<!-- druid包 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.20</version>
</dependency>
<!-- mysql 数据库驱动. -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-plus包-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>2.2.0</version>
</dependency>
<!--web包-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
1、Security的配置
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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;

/**
 * security的配置
 */
@Configuration
@EnableWebSecurity //开启Spring的security
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 密码使用加密
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * 密码授权模式
     * @return
     * @throws Exception
     */
    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
      return  super.authenticationManager();
    }

    /**
     * 授权规则配置
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()   //定义权限规则
                .antMatchers("/login").permitAll() //放行登录路径
//                .antMatchers("/course/list").permitAll()
                .anyRequest().authenticated()  //其它所有路径都要认证才能访问
                .and().formLogin() //允许表单登录
                .successForwardUrl("/login/success")  //登录成功跳转路径
                .and().csrf().disable();  //关闭跨域伪造检查

    }
}
2、授权服务器配置

写一个类MyAuthorizationServerConfigurerAdapter,继承AuthorizationServerConfigurerAdapter覆写三个configure方法

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
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.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import javax.sql.DataSource;
import java.util.Arrays;


/**
 * 授权服务器配置器适配器
 */
@Configuration
@EnableAuthorizationServer //开启授权服务器配置
public class MyAuthorizationServerConfigurerAdapter extends AuthorizationServerConfigurerAdapter {

    //访问数据库
    @Autowired
    private DataSource dataSource;

    //密码加密模式
    @Autowired
    private PasswordEncoder passwordEncoder;

    //1.客户端详情配置======================================================
    /**
     * 客户详情服务
     * @return
     */
    @Bean
    public ClientDetailsService clientDetailsService(){
        //Jdbc客户端详细信息服务,操作数据库
        JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
        //因为要对比数据库的信息,所以要设置密码加密的模式
        jdbcClientDetailsService.setPasswordEncoder(passwordEncoder);
        return jdbcClientDetailsService;
    }

    /**
     * 1.客户端详细信息服务配置
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        /*//Jdbc客户端详细信息服务,操作数据库
        JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
        //因为要对比数据库的信息,所以要设置密码加密的模式
        jdbcClientDetailsService.setPasswordEncoder(passwordEncoder);*/

        //客户详细信息
        clients.withClientDetails(clientDetailsService());
    }

    //2.服务端点:授权码,令牌管理配置===================================
    //认证管理器
    @Autowired
    private AuthenticationManager authenticationManager;


    /**
     * 定义 JdbcAuthorizationCodeServices 授权码的服务,基于数据库存储
     * @return
     */
    @Bean
    public AuthorizationCodeServices authorizationCodeServices(){
        JdbcAuthorizationCodeServices jdbcAuthorizationCodeServices = new JdbcAuthorizationCodeServices(dataSource);
        return jdbcAuthorizationCodeServices;
    }

    /**
     * 配置令牌的存储
     * @return
     */
    @Bean
    public TokenStore tokenStore(){

        //使用JWT令牌,不需要发请求
        JwtTokenStore jwtTokenStore = new JwtTokenStore(jwtAccessTokenConverter());
        return jwtTokenStore;

        //通过发请求访问的模式获取token
        //return new InMemoryTokenStore();
    }

    /**
     * Jwt访问令牌转换器
     * @return
     */
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        //Jwt访问令牌转换器
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        //设置签名密钥
        jwtAccessTokenConverter.setSigningKey(Oauth2Constant.SIGNINGKEY);
        return jwtAccessTokenConverter;
    }

    /**
     * 定义AuthorizationServerTokenServices ,令牌的服务配置
     * @return
     */
    @Bean
    public AuthorizationServerTokenServices tokenServices(){
        //创建默认的令牌服务
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        //指定客户端详情配置
        defaultTokenServices.setClientDetailsService(clientDetailsService());
        //支持产生刷新token
        defaultTokenServices.setSupportRefreshToken(true);

        //设置token增强 - 设置token转换器
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter()));
        defaultTokenServices.setTokenEnhancer(tokenEnhancerChain);  //jwtAccessTokenConverter()

        //token存储方式
        defaultTokenServices.setTokenStore(tokenStore());
        return defaultTokenServices;
    }

    /**
     * 2.授权服务器端点配置器
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //密码授权模式需要
        endpoints.authenticationManager(authenticationManager)
                //授权码模式服务
                .authorizationCodeServices(authorizationCodeServices())
                //配置令牌管理服务
                .tokenServices(tokenServices())
                //允许post方式请求
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);

    }

    //3.授权服务安全配置,url是否放行等========================================
    /**
     * 3.授权服务器安全配置
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.
                checkTokenAccess("permitAll()") //对应/oauth/check_token  路径公开
                .allowFormAuthenticationForClients();  //允许客户端进行表单身份验证,使用表单认证申请令牌
    }
}
3、UserDetails对象

写一个类UserDetailsServiceImpl实现UserDetailsService接口,实现loadUserByUsername方法

import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
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.Repository;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Repository
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private LoginMapper loginMapper;

    @Autowired
    private SystemFeignClient systemFeignClient;

    /**
     * 根据用户名查询数据,返回UserDetails对象
     *
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //查询自己数据库中的用户信息
        Login loginUser = loginMapper.selectByUsername(username);
        if (loginUser == null) {
            throw new UsernameNotFoundException("用户名不存在");
        }
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        if(loginUser.getType().intValue() == 0){ //如果是后台用户,就获取权限
            //调用feign接口
            AjaxResult ajaxResult = systemFeignClient.selectPermissionByLongId(loginUser.getId());
            List<String> permissions = (List<String>) ajaxResult.getResultObj();
            //权限转换为GrantedAuthority集合
            grantedAuthorities =  permissions.stream().map(permission ->
                    new SimpleGrantedAuthority(permission)
            ).collect(Collectors.toList());
        }

        //封装到UserDetails中(将用户对象转为json)
        User user = new User(JSON.toJSONString(loginUser), loginUser.getPassword(), grantedAuthorities);
        return user;
    }

}

资源服务配置

依赖

<!--oauth2包,包含security包-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
1、资源服务配置
import org.springframework.context.annotation.Bean;
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.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;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

@Configuration
@EnableResourceServer //开启资源服务
@EnableGlobalMethodSecurity(prePostEnabled = true)  //开启方法授权
public class MyResourceServerConfigurerAdapter extends ResourceServerConfigurerAdapter {

    //1.1资源服务令牌验证服务,通过远程校验令牌
    /*@Bean
    public ResourceServerTokenServices resourceServerTokenServices(){
        //使用远程服务请求授权服务器校验token , 即:资源服务和授权服务器不在一个主机
        RemoteTokenServices services = new RemoteTokenServices();
        //授权服务地址 , 当浏览器访问某个资源时就会调用该远程授权服务地址去校验token
        //要求请求中必须携带token
        services.setCheckTokenEndpointUrl("http://localhost:1100/oauth/check_token");
        //客户端id,对应认证服务的客户端详情配置的clientId
        services.setClientId("webapp");
        //密钥,对应认证服务的客户端详情配置的client_secret
        services.setClientSecret("123");
        return services;
    }*/

    /**
     * 资源服务器安全配置器
     * @param resources
     * @throws Exception
     */
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {

        //我的资源名称是什么
        resources.resourceId("course");

        //通过jwt方式解密私钥,不需要发送请求
        resources.tokenStore(tokenStore());

        //通过发请求的方式获取
        //resources.tokenServices(resourceServerTokenServices());

    }

    /**
     * 配置令牌的存储
     * @return
     */
    @Bean
    public TokenStore tokenStore(){

        //使用JWT令牌,不需要发请求
        JwtTokenStore jwtTokenStore = new JwtTokenStore(jwtAccessTokenConverter());
        return jwtTokenStore;

        //通过发请求访问的模式获取token
        //return new InMemoryTokenStore();
    }

    /**
     * Jwt访问令牌转换器
     * @return
     */
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        //Jwt访问令牌转换器
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        //设置签名密钥
        jwtAccessTokenConverter.setSigningKey(Oauth2Constant.SIGNINGKEY);
        return jwtAccessTokenConverter;
    }

    /**
     * Http安全性
     * @param http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                //校验scope必须为hrm , 对应认证服务的客户端详情配置的clientId
                .antMatchers("/**").access("#oauth2.hasScope('hrm')")
                .and().csrf().disable();   //关闭跨域伪造检查

    }
}
2、controller层方法授权

@PreAuthorize(“hasAuthority(‘course:online’)”) //course:online具体的权限。
需要资源服务配置类上开启方法授权:@EnableGlobalMethodSecurity(prePostEnabled = true) //开启方法授权

@RestController
@RequestMapping("/course")
public class CourseController {
    
    @Autowired
    public ICourseService courseService;

    /**
     * 课程上线
     * @PreAuthorize("hasAuthority('course:online')") //方法授权
     * @param courseIdd
     * @return
     */
    @PreAuthorize("hasAuthority('course:online')") //方法授权-------
    @RequestMapping(value = "/onLineCourse/{id}", method = RequestMethod.POST)
    public AjaxResult onLineCourse(@PathVariable("id") Long courseIdd) {
        courseService.onLineCourse(courseIdd);
        return new AjaxResult();
    }
}    
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值