Spring Boot项目系列(4)集成Spring Security ,Oauth2实现项目级别的权限控制和鉴权管理

前言

    项目中出现的,接口权限问题,和第三方鉴权功能的简单实现。技术采用spring boot+spring security+oauth2相关技术,做一个简单的权限管理。本项目,只适合用来学习搭建项目,并不适用于直接用于项目开发。还需要加点改造。本项目github地址:

https://gitee.com/shen_wen_jia/learning-projects/tree/master/springsecurity-oauth2

    
 
 
整体项目架构

在这里插入图片描述

jar包导入

 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
		<!--spring security实现的相关jar包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!-- oauth2.0支持组件 -->
        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>2.1.4.RELEASE</version>
        </dependency>
        <!--模板引擎,搭建简单的登录界面-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <!--工具包,主要用了一个JSON的功能-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.3.7</version>
        </dependency>

    </dependencies>

1.spring secutity实现接口的权限访问控制

1)项目搭建

application.yml配置

mvc:
  view:
    suffix: html
    prefix:
server:
  port: 8080
  servlet:
    context-path: /oauth

使用

    首先,使用spring security搭建一个简单的登陆跳转,接口权限验证功能。使用到的代码文件有SecurityConfig.java-用来做spring security的详细配置。MyUserDetailService.java-用来做登陆逻辑。LoginController.java-用来做简单的登陆界面跳转,使用th模板引擎搭建了一个简单的登陆界面。UserController.java-用来做简单的接口权限项目测试。

SecurityConfig.java

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/","/login","/hello","/oauth/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login_p")
                //登录处理接口
                .loginProcessingUrl("/doLogin")
                //定义登录时,用户名的 key,默认为 username
                .usernameParameter("username")
                //定义登录时,用户密码的 key,默认为 password
                .passwordParameter("password")
                //成功/失败登录之后的返回逻辑
                .successHandler(new AuthenticationSuccessHandler() {
                    private Logger logger = LoggerFactory.getLogger(this.getClass());
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
                        resp.setContentType("application/json;charset=utf-8");
                        PrintWriter out = resp.getWriter();
                        System.out.println(authentication);
                        out.write(JSONUtil.toJsonPrettyStr(authentication));
                        out.flush();
                    }
                })
                .failureHandler(new AuthenticationFailureHandler(){
                    private Logger logger = LoggerFactory.getLogger(this.getClass());

                    @Override
                    public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException, ServletException {
                        resp.setContentType("application/json;charset=utf-8");
                        PrintWriter out = resp.getWriter();
                        out.write(e.toString());
                        out.flush();
                    }
                })
                .permitAll()
                .and()
                //退出登录的登录接口。
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessHandler(new LogoutSuccessHandler() {
                    @Override
                    public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
                        resp.setContentType("application/json;charset=utf-8");
                        PrintWriter out = resp.getWriter();
                        out.write("logout success");
                        out.flush();
                    }
                })
                .permitAll();
    }

    /**
     * 密码加密解密的配置
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder(){

        return new BCryptPasswordEncoder();
    }

    /**
     * 需要配置这个支持password模式-oauth2的相关配置
     */
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

    配置中调用了前端自己定义的登陆逻辑-index.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
        xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
   <head>
           <title>Spring Security Example </title>
       </head>
   <body>
       <div th:if="${param.error}">
               Invalid username and password.
           </div>
       <div th:if="${param.logout}">
               You have been logged out.
           </div>
       <form th:action="@{/doLogin}" method="post">
               <div><label> User Name : <input type="text" name="username"/> </label></div>
               <div><label> Password: <input type="password" name="password"/> </label></div>
               <div><input type="submit" value="Sign In"/></div>
           </form>
   </body>
</html>

MyUserDetailService.java:用户登陆的判断逻辑在这里实现

    这里的登陆判断逻辑,最好还是根据自己的数据库设计,来加入相应的逻辑判断。

@Component
public class MyUserDetailService implements UserDetailsService {
    Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    private PasswordEncoder passwordEncoder;


    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        logger.info("登陆用户名:{}",s);
        String password  = "123";
        password = passwordEncoder.encode(password);
        List<SimpleGrantedAuthority> authorities = new ArrayList<>(8);
        //这里加上用户的权限逻辑,这里我使用-来划分资源的细化权限
        SimpleGrantedAuthority authority = new SimpleGrantedAuthority("user-add");
        authorities.add(authority);
        //角色权限,使用角色权限时,需要在加色的前方加上ROLE_,代表一个角色权限
        SimpleGrantedAuthority authority2 = new SimpleGrantedAuthority("ROLE_admin");
        authorities.add(authority2);
        
        return new User(s,password, true, true, true, true,
                authorities);
    }
}

UserController.java :用户的测试,使用注解管理接口的权限

@RestController
@RequestMapping("user")
public class UserController {
    private Logger logger = LoggerFactory.getLogger(PermisssionController.class);

    @GetMapping
    //使用本注解来管理一个接口所应该拥有的权限
    @PreAuthorize("hasPermission('user','add') or hasRole('admin')" )
    public String get(){
        logger.info("访问成功");
        return "success";
    }
}

MyPermissionEvaluator.java:测试所用 的接口的解析逻辑,比对接口的权限,返回boolean,是否能够调用。

@Configuration
public class MyPermissionEvaluator implements PermissionEvaluator {
    private Logger logger = LoggerFactory.getLogger(MyPermissionEvaluator.class);
    @Override
    public boolean hasPermission(Authentication authentication, Object o, Object o1) {
        boolean accessable = false;
        logger.info(authentication.getPrincipal().toString());

        if(authentication.getPrincipal().toString().compareToIgnoreCase("anonymousUser") != 0){
            String privilege = o+"-"+o1;
            for (GrantedAuthority authority : authentication.getAuthorities()) {
                if(privilege.equalsIgnoreCase(authority.getAuthority())){
                    accessable = true;
                    break;
                }
            }
        }
        return accessable;
    }

    @Override
    public boolean hasPermission(Authentication authentication, Serializable serializable, String s, Object o) {
        return false;
    }
}
构建结束

    依靠上述的项目构建结果,可以看出,我们搭建一个简单的用户接口的权限验证框架。相关逻辑如下图所示

在这里插入图片描述

2)项目测试

待跟新

访问接口,自动跳转登录界面

在这里插入图片描述

失败返回

在这里插入图片描述

成功返回

在这里插入图片描述

访问接口:在没有登陆之前,是直接返回404的

在这里插入图片描述

2.spring security 实现oauth2的相关鉴权服务

1)相关配置

    spring security框架提供了对oauth2的支持,这里简单实现一下,oauth2的相关。主要使用到2个配置文件OAuth2ServerConfig-提供相关的oauth2的相关配置,ResourceServerConfig-配置oauth2的资源访问接口(配置哪些接口是需要通过oauth2鉴权之后才能访问的)。PermisssionController-测试controller层

OAuth2ServerConfig

@Configuration
@EnableAuthorizationServer
public class OAuth2ServerConfig extends AuthorizationServerConfigurerAdapter {

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer
                .realm("oauth2-resources")
                //code授权添加
                .tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()")
                //allow check token
                .allowFormAuthenticationForClients();
    }
    /**
     * 注入authenticationManager
     * 来支持 password grant type
     */
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager)
                //允许 GET、POST 请求获取 token,即访问端点:oauth/token
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
    }
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("demoApp")
                .secret(passwordEncoder.encode("demoAppSecret"))
                .redirectUris("http://baidu.com")//code授权添加
                .authorizedGrantTypes("authorization_code","client_credentials", "password", "refresh_token")
                .scopes("all")
                .resourceIds("oauth2-resource")
                .accessTokenValiditySeconds(1200)
                .refreshTokenValiditySeconds(50000);
    }

ResourceServerConfig:配置了需要的接口服务

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.requestMatchers().antMatchers("/per/**")
                .and()
                .authorizeRequests()
                .antMatchers("/per/**").authenticated();
    }
}

PermisssionController:测试所用的权限层

@RestController
@RequestMapping("per")
public class PermisssionController {
    private Logger logger = LoggerFactory.getLogger(PermisssionController.class);

    @GetMapping
    public String get(){
        logger.info("访问成功");
        return "success";
    }
}
2)测试

第一步:获取token

密码模式

url:

http://localhost:8080/oauth/oauth/token?username=admin&password=123&grant_type=password&client_id=demoApp&client_secret=demoAppSecret

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oHmzBtUr-1602921484289)(C:\Users\swj\AppData\Roaming\Typora\typora-user-images\image-20201017151507918.png)]

客户端模式

url:

http://localhost:8080/oauth/oauth/token?grant_type=client_credentials&client_id=demoApp&client_secret=demoAppSecret

在这里插入图片描述

第二步访问接口

错误情况:

在这里插入图片描述

正确的情况

在这里插入图片描述

结语

路漫漫其修远兮,吾将上下而求索!
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值