springBoot 集成 springSecurity


前言

至于什么是Spring security ,主要用户认证和授权。即我们常说的,用户只有登录了才能进行其他操作,没有登录的话就重定向到登录界面。有的用户有权限执行某一操作,而有的用户不能执行则是授权。算是一个项目安全框架。和shiro 框架一样。二者的不同大家可以百度下。Spring security 是Spring家族的一员,所以Springboot算是对Spring security 进行的天然的支持,今天学习了下,做个总结


一、添加maven依赖

   <!--Security框架-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency> 
        <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>
        </dependency>
		  <!--thymeleaf与Spring Security整合的依赖 https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity4 -->
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity5</artifactId>
            <version>3.0.4.RELEASE</version>
        </dependency>
        <!--thymeleaf-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
server:
  port: 8080
  servlet:
    encoding:
      charset: utf-8
      enabled: true
spring:
  servlet:
    multipart:
      maxFileSize: 102400000
      maxRequestSize: 102400000
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
    encoding: UTF-8
    cache: false        # 禁用缓存
    mode: HTML5    # 非严格型检查 默认是html 严格检查
    servlet:
      content-type: text/html

项目结构

javal
在这里插入图片描述

二、创建SecurityConfig.java配置类

代码如下

mport com.longWind.common.security.handle.MyAccessDeniedHandler;
import com.longWind.common.security.handle.MyAuthenticationFailureHandler;
import com.longWind.common.security.service.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;


/**
 * @author wanglei
 * @date 2021年05月12日 21:27
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyAccessDeniedHandler deniedHandler;


    @Autowired
    private UserDetailsServiceImpl userDetailsService;

//    @Autowired
//    private PersistentTokenRepository persistentTokenRepository;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //表单提交
        http.formLogin()
                //设置登录页面from表单的用户名,密码的别名
                // 用户名:<input type="text" name="username123"/><br/>
                //  密码:<input type="password" name="password123"/><br/>
//                .usernameParameter("username123")
//                .passwordParameter("password123")
                //拦截登录请求
                .loginProcessingUrl("/login")
                //自定义登录页面
                .loginPage("/showLogin")
                //登录成功后跳转页面,post请求
                .successForwardUrl("/toMain")
                //登录成功后的处理器不能和successForwardUrl共存
//                .successHandler(new MyAuthenticationSuccessHandler("/main.html"))

//                .failureForwardUrl("/toError")
                //登录失败的处理器不能和failureForwardUrl共存
                .failureHandler(new MyAuthenticationFailureHandler("/error.html"))
        ;


        //授权认证
        http.authorizeRequests()
                //login.html不需要认证
//                .antMatchers("/login.html").permitAll()
                .antMatchers("/showLogin").access("permitAll()")
//                .antMatchers("/error.html").permitAll()
                //所有的都可以用access来实现
                .antMatchers("/error.html").access("permitAll()")
                //放行静态资源
                .antMatchers("/image/**","/js/**","/css/**").permitAll()
                //正则表达式放行png结尾的静态资源
                // .regexMatchers(".+[.]png").permitAll()
                //指定请求方式做拦截
                //.regexMatchers(HttpMethod.POST,".toDome").permitAll()
//                .mvcMatchers("/toDome").servletPath("/xxx").permitAll()
                //权限严格区分大小写,拥有admin权限才能访问/main1.html
//                .antMatchers("/main1.html").hasAnyAuthority("admin")
                //拥有admin,admiN等配置的任何一个权限都可以访问/main1.html
//                .antMatchers("/main1.html").hasAnyAuthority("admin,admiM")
                //拥有abc角色可以访问/main.html
//                .antMatchers("/main1.html").hasRole("abcd")
//                .antMatchers("/main1.html").access("hasRole('abc')")
                //拥有abc,abcd等配置的任何一个角色都可以访问/main.html
//                .antMatchers("/main1.html").hasAnyRole("abc,abcd")
                //只有IP地址为127.0.0.1才能访问/main1.html
//                .antMatchers("/main1.html").hasIpAddress("127.0.0.1")
                //所有请求都被拦截,必须登录后才能访问
                .anyRequest().authenticated ()
                //自定义实现url权限控制,
//                  .anyRequest().access("@myServiceImpl.hasPermission(request,authentication)")
        ;
        //关闭csrf防护
//        http.csrf().disable();
        //异常处理 自定义权限不足处理
        http.exceptionHandling().accessDeniedHandler(deniedHandler);
//        //记住我
//        http.rememberMe()
//                //设置失效时间 单位 秒
////                .tokenValiditySeconds(60)
//                //自定义前端登录页面记住我的name值
////                .rememberMeParameter("")
//                //自定义登录逻辑
//                .userDetailsService(userDetailsService)
//                //持久层对象
//                .tokenRepository(persistentTokenRepository);
        //退出登录跳转页面
        http.logout()
                //退出成功后的跳转页面
                .logoutSuccessUrl("/login.html");
    }


//    @Bean
//    public PersistentTokenRepository getPersistentTokenRepository(){
//        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
//        jdbcTokenRepository.setJdbcTemplate(jdbcTemplate);
//        //自动建表,第一次启动的时候需要,第二次启动注释
//        jdbcTokenRepository.setCreateTableOnStartup(true);
//        return new JdbcTokenRepositoryImpl();
//    }

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


}

三、自定义权限不足处理

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 自定义权限不足处理
 * @author wanglei
 * @date 2021年05月13日 21:32
 */
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
        //设置相应状态码
        response.setStatus(HttpServletResponse.SC_FORBIDDEN );
        response.setHeader("Content-Type","application/json;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.write("{\"status\":\"error\",\"mag\":\"权限不足请联系管理员 \"}");
        writer.flush();
        writer.close();
    }
}

四、自定义登录失败的处理器MyAuthenticationFailureHandler.java

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author wanglei
 * @date 2021年05月13日 13:40
 */
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
    private  String url;

    public MyAuthenticationFailureHandler(String url){
        this.url=url;
    }
    @Override
    public void onAuthenticationFailure(HttpServletRequest request,
           HttpServletResponse response,AuthenticationException e) throws IOException, ServletException {
        response.sendRedirect(url);
    }
}

五、自定义登录成功的处理器MyAuthenticationSuccessHandler.java

import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author wanglei
 * @date 2021年05月13日 13:27
 */
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    private  String url;

    public MyAuthenticationSuccessHandler(String url){
        this.url=url;
    }
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {

    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        System.out.println(request.getRemoteAddr());
        User user = (User) authentication.getPrincipal();
        System.out.println(user.getUsername());
        System.out.println(user.getAuthorities());
        System.out.println(user.getPassword());
        response.sendRedirect(url);
    }
}

五、登录验证逻辑处理类UserDetailsServiceImpl.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
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.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

/**
 * 自定义登录验证逻辑处理类
 * @author wanglei
 * @date 2021年05月12日 21:31
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private PasswordEncoder pw;


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //1.查询数据库判断用户名是否存在,如果不存在抛出异常UsernameNotFoundException
        if (!"admin".equals(username)){
            throw new UsernameNotFoundException("用户名不存在");
        }
        //2.把查询的密码(注册时已经加密)进行解析,或者直接把密码放入构造函数
        String password=pw.encode("123");
        return new User(username,password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal," +
                //ROLE_ 固定写法,配置角色
                "ROLE_abc,/main.html"));
    }
}

六、access 自定义权限拦截

//自定义实现url拦截
.anyRequest().access("@myServiceImpl.hasPermission(request,authentication)")
import org.springframework.security.core.Authentication;

import javax.servlet.http.HttpServletRequest;

/**
 *
 * @author wanglei
 * @date 2021年05月13日 21:48
 */
public interface MyService {

    boolean hasPermission(HttpServletRequest request, Authentication authentication);
}
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.Collection;

/**
 * 自定义权限
 * @author wanglei
 * @date 2021年05月13日 21:51
 */
@Service
public class MyServiceImpl implements MyService{
    @Override
    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
        Object principal = authentication.getPrincipal();
        if (principal instanceof UserDetails){
            UserDetails userDetails =(UserDetails)principal;
            Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
            return authorities.contains(new SimpleGrantedAuthority(request.getRequestURI()));
        }
        return false;
    }
}

测试

LoginController

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;


/**
 * 系统授权接口
 * @author wanglei
 * @date 2021年05月09日 16:33
 */

@Slf4j
@Controller
public class LoginController {

    @RequestMapping("/login")
    public String login(){
        return "redirect:main.html";
    }

//    @Secured("ROLE_abc")
    //PreAuthorize的表达式在方法执行之前做权限判断
//    @PreAuthorize("hasRole('abc')")
    @RequestMapping("/toMain")
    public String toMain(){
        return "redirect:main.html";
    }

    @RequestMapping("/toError")
    public String toError(){
        return "redirect:error.html";
    }


    @RequestMapping("/toDome")
    public String toDome(){
        return "dome";
    }

    @RequestMapping("/showLogin")
    public String showLogin(){
        return "login";
    }
}

login.html

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--自定义登录页面-->
<form action="/login" method="post">
    用户名:<input type="text"  name="username"/><br/>
    密码:<input type="password" name="password"/><br/>
    记住我:<input type="checkbox" name="remember-me" value="true"/><br/>
    <input type="submit" value="登录"/>
</form>
</body>
</html>

main.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
登录成功!!!!!!!!!!!!!!!
<a href="/logout">退出</a>
<a href="/main1.html">跳转</a>
</body>
</html>

main1.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
main1
</body>
</html>

权限测试

deome.html

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html"
                xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>springboot-thymeleaf demo</title>
</head>

<body>
<!--自定义登录页面-->
<form action="/login" method="post">
    <input type="hidden" th:value="${_csrf.token}" name="_csrf" th:if="${_csrf}">
    用户名:<input type="text"  name="username"/><br/>
    密码:<input type="password" name="password"/><br/>
    记住我:<input type="checkbox" name="remember-me" value="true"/><br/>
    <input type="submit" value="登录"/>
</form>
</body>
</html>

login.html _csrf关闭csrf测试

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html"
                xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>springboot-thymeleaf demo</title>
</head>

<body>
<!--自定义登录页面-->
<form action="/login" method="post">
    <input type="hidden" th:value="${_csrf.token}" name="_csrf" th:if="${_csrf}">
    用户名:<input type="text"  name="username"/><br/>
    密码:<input type="password" name="password"/><br/>
    记住我:<input type="checkbox" name="remember-me" value="true"/><br/>
    <input type="submit" value="登录"/>
</form>
</body>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值