Spring Security的实例

学自:b站的博主:“一岁就会编程”的在线智能办公系统的教学视频

创建一个Spring Boot项目

依赖可以自己导入或创建时添加上

pom文件里添加依賴

<dependencies>
        <!--SpringSecurity組件-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!--web組件-->
        <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>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc8</artifactId>
            <version>12.1.0.2.0</version>
        </dependency>
        <!--SpringSecurity可以再一些视图技术中进行控制显示效果 在非前后端分离且使用 
         Springboot的项目中多使用Thymeleaf作为视图展示技术 -->
        <!--Thymeleaf对Spring Security的支持都放在thymeleaf-extras-springsecurity中-->
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
    </dependencies>

至于建的包,类什么的用图片看吧,写的麻烦

SecurityConfig类

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    MyAccessDeniedHandler myAccessDeniedHandler;

    @Autowired
    UserDetailsService userDetailsService;

    @Autowired
    private DataSource dataSource;

    @Autowired
    private PersistentTokenRepository persistentTokenRepository;

    @Bean
    public PasswordEncoder getPw(){
        return new BCryptPasswordEncoder();
    }
    @Bean
    public PersistentTokenRepository getPersistentTokenRepository(){
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        //自动建表,第一次启动时候需要,第二次启动注释掉
        //jdbcTokenRepository.setCreateTableOnStartup(true);
        return jdbcTokenRepository;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //表单提交
    http.formLogin()
            //
            .usernameParameter("username123")
            .passwordParameter("password123")
            //当发现(/login)时认为是登录,必须和窗体提交的地址一样,去执行UserDetailsServiceImpl
            .loginProcessingUrl("/login")
            //自定义登录页面
            .loginPage("/showLogin")
            //登陆成功后跳转页面必须是post请求
            .successForwardUrl("/toMain")
            //登陆成功后处理器,不能和successForwardUrl共存
            // .successHandler(new MyAuthenticationSuccessHandler("/main.html"))
            //登录失败后跳转页面,post请求
            .failureForwardUrl("/toError");
            //登陆失败后处理器,不能和failureForwardUrl共存
            //.failureHandler(new MyAuthenticationFailureHandler("/error.html"));
    //授权认证
    http.authorizeRequests()
            //error.html不需要被认证
            .antMatchers("/error.html").permitAll()
            //login.html不需要被认证
            .antMatchers("/showLogin").permitAll()
            //.antMatchers("/js/**","/css/","/images/**").permitAll()
            //.antMatchers("/**/*.png").permitAll()
        //正则表达式匹配
            //.regexMatchers(".+[.]png").permitAll()
            //.regexMatchers("/demo").permitAll()
            //.regexMatchers(HttpMethod.GET,"/demo").permitAll()
        //mvc匹配servletPath为特有方法,其他2种匹配方法没有
            //看 配置文件.properties,用来规定访问demo,只能通过  开放 (/xxx路径)访问  该路径等效于  .antMatchers("/xxx/demo").permitAll()
            //.mvcMatchers("/demo").servletPath("/xxx").permitAll()
        //权限判断
            //hasAuthority("admin") 加角色权限,只有admin用户才能访问,严格遵守大小写
            //.antMatchers("/main1.html").hasAuthority("admin")
            //.antMatchers("/main1.html").hasAnyAuthority("admin","admiN")
        //对角色进行判断
            //.antMatchers("/main1.html").hasRole("abc")
            //hasRole('abc')和permitAll()或者其他最终都是通过access()方法来实现的
            //.antMatchers("/main1.html").access("hasRole('abc')")
            //.antMatchers("/main1.html").hasAnyRole("abC,abc")
        //IP地址判断
            //本机IP地址用hasIpAdress(),不是本机的话request.getRemoteAddr();
            //.antMatchers("/main1.html").hasIpAddress("127.0.0.1")
        //access结合自定义方法实现权限控制
            //.anyRequest().access("@myServiceImpl.hasPermission(request,authentication)");
            //所有请求都必须被认证,必须登录之后被访问
            .anyRequest().authenticated();
    //CSRF跨站请求伪造,也被称为“OneClick Attack”或者“session Riding”通过伪造用户请求访问受信任站点的非法请求访问
    //关闭csrf防护
    http.csrf().disable();

    //异常处理
    http.exceptionHandling()
            .accessDeniedHandler(myAccessDeniedHandler);
    //RememberMe功能,用户只需要在登录时添加remember-me复选框,取值为true,spring security会自动把用户信息存储到数据源中,以后就可以不登录进行访问
    //要实现该功能要导入Spring-JDBC,一般来说导入mybatis也一样,
    //(记住我)功能
    http.rememberMe()
            //失效时间,单位秒
            .tokenValiditySeconds(60)
            //.rememberMeParameter()
            //自定义登录逻辑
            .userDetailsService(userDetailsService)
            //持久层对象
            .tokenRepository(persistentTokenRepository);
    //退出登录
    http.logout()
            //默认的
            .logoutUrl("logout")
            //自定义的
            //.logoutUrl("/user/logout")
            //退出登录跳转页面
            .logoutSuccessUrl("/login.html");
}

}

 LoginController类

@Controller
public class LoginController {

    /*
    * 登录*/
    /*@RequestMapping("login")
    public String login(){
        System.out.println("执行登录方法");
        return "redirect:main.html";
    }*/

    /*跳转成功页面*/
    //@Secured()是专门用于判断是否具有角色的,能写在方法或类上,参数要以ROLE_开头,开启批注用@EnableGlobalMethodSecurity(securedEnabled = true)
    //@Secured("ROLE_abc")

    //@PreAuthorize/@PostAuthorize都是方法或类级别批注,开启批注用@EnableGlobalMethodSecurity(prePostEnabled = true)
    //@PreAuthorize表示访问方法或类在执行之前先判断权限,大多数情况下都是使用这个批注,批注的参数和access()方法参数取值相同,都是权限表达式。
           //PreAuthorize的表达式允许ROLE_开头,也可以不以ROLE_开头,配置类不允许ROLE_开头
    //@PostAuthorize表示方法或类执行结束后判断权限,此批注很少被使用到
    //@PreAuthorize("hasRole('abc')")
    @PreAuthorize("hasRole('ROLE_abc')")
    @RequestMapping("toMain")
    public String toMain(){
        return "redirect:main.html";
    }


    /*跳转失败页面*/
    @RequestMapping("toError")
    public String toError(){
        return "redirect:error.html";
    }

    /*跳转失败页面*/
//    @GetMapping("demo")
//    @ResponseBody
//    public String demo(){
//        return "demo";
//    }
    /*页面跳转*/
    @RequestMapping("demo")
    public String demo(){
        return "demo";
    }

    /*页面跳转*/
    @RequestMapping("showLogin")
    public String showLogin(){
        return "login";
    }
}

MyAccessDeniedHandler类

@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
    AccessDeniedException e) throws IOException, ServletException {
        //设置响应状态代码
        httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
        httpServletResponse.setHeader("Content-Type","application/json;charset=utf-8");
        PrintWriter writer = httpServletResponse.getWriter();
        writer.write("{\"status\":\"error\",\"msg\":\"权限不足\"}");
        writer.flush();
        writer.close();
    }
}

MyAuthenticationFailureHandler类

//自定义登录失败的处理器
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
    private String url;

    public MyAuthenticationFailureHandler(String url) {
        this.url = url;
    }

    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
    AuthenticationException e) throws IOException, ServletException {
        httpServletResponse.sendRedirect(url);
    }
}

MyAuthenticationSuccessHandler类

//自定义登录成功的处理器
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    private String url;

    public MyAuthenticationSuccessHandler(String url) {
        this.url = url;
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
    Authentication authentication) throws IOException, ServletException {
        User user = (User) authentication.getPrincipal();
        System.out.println(user.getUsername());
        //考虑到安全性,密码输出为null
        System.out.println(user.getPassword());
        //权限
        System.out.println(user.getAuthorities());
        httpServletResponse.sendRedirect(url);
    }
}


MyService类 

//access结合自定义方法实现权限控制
public interface MyService {
    boolean hasPermission(HttpServletRequest request, Authentication authentication);
}


MyServiceImpl类

//access结合自定义方法实现权限控制
@Service
public class MyServiceImpl implements MyService {
    @Override
    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
        Object object = authentication.getPrincipal();
        if(object instanceof UserDetails){
            UserDetails userDetails = (UserDetails) object;
            Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
            return authorities.contains(new SimpleGrantedAuthority(request.getRequestURI()));
        }
        return false;
    }
}


UserDetailsServiceImpl类 

@Service
/*public class UserDetailsServiceImpl {*/
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private PasswordEncoder pw;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println("执行了loadUserByUsername");
        //查询数据库判断用户名是否存在,如果不存在就会抛出UsernameNotFoundException异常
        if(!"admin".equals(username)){
            throw new UsernameNotFoundException("不存在");
        }
        //把查詢出來的密碼(註冊時已經加密)進行解析,或者直接把密碼放入構造方法
        String password = pw.encode("123");
//        System.out.println(password);
//        System.out.println(pw.matches("123",password));
        //ROLE_abc:定义角色,ROLE_  判断条件 abc是角色,在SecurityConfig类里看下一步骤
        return new User(username,password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal,ROLE_abc,"+
                "/main.html,/insert,/delete"));
        //  (/insert,/delete")权限判断
    }
}

satic里的

error.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
操作失败,请重新登录<a href="login.html">跳转</a>
</body>
</html>

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/login" method="post">
    用戶名:<input type="text" name="username123" /><br/>
    密码:<input type="password" name="password123" /><br/>
    记住我:<input type="checkbox" name="remember-me"/><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>
登录成功
<br/>
<a href="/main1.html">跳转</a>
//自定义的
<!--<a href="/user/logout">退出</a>-->
//默认的
<a href="/logout">退出</a>
</body>
</html>

main1.html

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

templates里的

demo.html

<!DOCTYPE html>
<!--在HTML中引入thymeleaf命名空间和security命名空间-->
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
登录账号:<span sec:authentication="name"></span><br/>
登录账号:<span sec:authentication="principal.username"></span><br/>
凭证:<span sec:authentication="credentials"></span><br/>
权限和角色:<span sec:authentication="authorities"></span><br/>
客戶端地址:<span sec:authentication="detail.remoteAddress"></span><br/>
sessionId:<span sec:authentication="details.sessionId"></span><br/>


<br/>

通过权限判断:
<button sec:authorize="hasAuthority('/insert')">新增</button>
<button sec:authorize="hasAuthority('/delete')">刪除</button>
<button sec:authorize="hasAuthority('/update')">修改</button>
<button sec:authorize="hasAuthority('/select')">查看</button>
<br/>
通过角色判断:
<button sec:authorize="hasRole('abc')">新增</button>
<button sec:authorize="hasRole('abc')">刪除</button>
<button sec:authorize="hasRole('abc')">修改</button>
<button sec:authorize="hasRole('abc')">查看</button>
</body>
</html>

login.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/login" method="post">
    <input type="hidden" th:value="${_csrf.token}" name="_csrf" th:if="${_csrf}" />
    用戶名:<input type="text" name="username123" /><br/>
    密碼:<input type="password" name="password123" /><br/>
    記住我:<input type="checkbox" name="remember-me"/><br/>
    <input type="submit" value="登錄" />
</form>
</body>
</html>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值