SpringSecurity的基础操作,登录认证,授权认证等

news/2025/1/16 11:11:56/

文章目录

  • SpringSecurity
    • 1、SpringSecurity简介
    • 2、第一个SpringSecurity程序
    • 3、UserDetailsService接口
    • 4、 PasswordEncoder接口
    • 5、自定义登录逻辑
    • 6、自定义登录页面
    • 7、自定义登录成功和失败处理器
    • 8、授权配置
    • 9、角色认证
    • 10、记住我
    • 11、SpringSecurity整合thymeleaf
      • 11.1、获取登录信息
      • 11.2、权限判断
      • 11.3、注销配置
    • 12、csrf

SpringSecurity

1、SpringSecurity简介

​ Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作,就是一个授权(Authorization)和用户认证(Authentication)的安全框架。

SpringSecurity 官方文档:https://docs.spring.io/spring-security/reference/getting-spring-security.html

导入依赖:

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

2、第一个SpringSecurity程序

​ 创建一个初始化的SpringBoot项目,导入对应的依赖(web依赖、springsecurity依赖等),然后直接启动项目,当访问项目的某个资源时,会跳转到一个登陆页面。

image-20220113185521760

这个是springsecurity默认的登录页面,请求项目的资源时,如果没有登录就不会让你进行访问,而初始的登录账户是user,密码是项目启动时打印的,每次启动项目的密码不同。

image-20220113185640170

3、UserDetailsService接口

​ 如果需要自定义一个自己的登录逻辑,我们需要自己创建一个UserDetailsServiceImpl来实现UserDetailsService接口,然后重写loadUserByUsername方法,自定义登录逻辑。

@Service
public class UserDetailsServiceImpl implements UserDetailsService {// loadUserByUsername()  username参数是前端传来的登录账户参数// UserDetails 返回参数,也是一个接口,但是返回这个接口的实现类User@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {return null;}
}
public interface UserDetails extends Serializable {// 返回用户的权限集合,但是不能返回一个nullCollection<? extends GrantedAuthority> getAuthorities();// 获取密码String getPassword();// 获取账号String getUsername();// 判断用户账号是否过期boolean isAccountNonExpired();// 判断用户是否被锁定boolean isAccountNonLocked();// 判断用户密码是否过期boolean isCredentialsNonExpired();// 判断账户是否可用boolean isEnabled();}

4、 PasswordEncoder接口

​ 这是一个密码加密和匹配的一个接口,这就接口有很多实现类,对应的就是各种加密算法,但是官方推荐使用BCryptPasswordEncoder(), 一般会把这个实现类注入到spring容器中。

@Configuration
public class SecurityConfig {@Beanpublic PasswordEncoder getPasswordEncoder(){return new BCryptPasswordEncoder();}}
public interface PasswordEncoder {// 将一个字符串进行加密String encode(CharSequence rawPassword);// 第一个参数是原始的密码,第二个参数是加密后的算法,如果匹配就返回true,否则返回falseboolean matches(CharSequence rawPassword, String encodedPassword);default boolean upgradeEncoding(String encodedPassword) {return false;}
}

5、自定义登录逻辑

// 需要注入到spring容器中
@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate PasswordEncoder passwordEncoder;// loadUserByUsername()  username参数是前端传来的登录账户参数,前端的参数名必须为username,不能改变// UserDetails 返回参数,也是一个接口,但是返回这个接口的实现类User@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 1、 通过username从数据库中查询数据Admin admin = new Admin(1,"admin","123456"); // 模拟查询的数据if (admin==null){// 如果不存在就抛出异常throw new UsernameNotFoundException("用户名不存在");}// 返回一个user对象,第一个参数是参数username,// 第二个参数是数据库中查询的密码,这个参数必须通过passwordEncoder加密,不然也不会通过,数据库保存的密码一般都是加密了的,不会是明文密码// 第三个参数权限列表return new User(username,passwordEncoder.encode(admin.getAdminPassword()),AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));}
}

6、自定义登录页面

​ 如果没有设置自定义的登录页面,就会使用springsecurity默认的登录页面。

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{@Beanpublic PasswordEncoder getPasswordEncoder(){return new BCryptPasswordEncoder();}// 权限分配@Overrideprotected void configure(HttpSecurity http) throws Exception {// 拦截所有的请求http.authorizeRequests()// 为某些请求设置为所有人都可以访问.antMatchers("/","/index.html","/login.html","/rest.html","/assets/**","/fail.html").permitAll()// 任何请求都需要认证.anyRequest().authenticated();// 登录表单http.formLogin()// 自定义登录页面.loginPage("/login.html")// 设置前端的传入登录名的参数名,默认是username,可以修改为其他但是和前端对应.usernameParameter("user").passwordParameter("password")// 设置登录请求,如果是/login就认为是登录请求.loginProcessingUrl("/myLogin")// 登录成功的跳转页面/*设置登录成功的页面:方式一: defaultSuccessUrl("url",true),如果不设置第二个参数为true的话,如果访问的访问的是一个不存在的页面登录成功就会跳到这个不存在的url。方式二: successForwardUrl("url"),需要一个post请求,在MvcConfig设置的请求都是get请求,需要写一个controller重定向到对应的页面。*/.successForwardUrl("/toMain")// 登录失败的跳转页面,注意登录失败的请求也会被认证,所以需要将失败的请求的认证放行// 还有一个failureUrl()与failureForwardUrl()的区别在于,前者需要一个get请求,而后者需要一个post请求// .failureUrl("/fail.html").failureForwardUrl("/failLogin");// 关闭csrf防御http.csrf().disable();}
}

7、自定义登录成功和失败处理器

// 登录成功的处理器
.successHandler(new AuthenticationSuccessHandler() {// request、response// Authentication用户认证,可获得登录账号、密码和权限集合@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {// 重定向到指定页面response.sendRedirect(request.getContextPath()+"/main.html");}
})
// 登录失败的处理器
.failureHandler(new AuthenticationFailureHandler() {// request、response// exception异常处理@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {logger.info(exception);request.setAttribute("msg","登录失败!!");response.sendRedirect(request.getContextPath()+"/fail.html");}
});

处理器只能通过response请求从定向到某个页面,不能使用请求转发,只支持get请求的方法。

8、授权配置

​ 编写一个配置类SecurityConfig,这个继承 WebSecurityConfigurerAdapter类,然后重载这个类的方法。

@EnableWebSecurity  // 这个注解开启了Security
public class SecurityConfig extends WebSecurityConfigurerAdapter {}
// 授权配置,链式编程
@Override
protected void configure(HttpSecurity http) throws Exception {// 拦截所有的请求http.authorizeRequests()// 为某些请求设置为所有人都可以访问.antMatchers("/","/index.html","/login.html","/rest.html","/assets/**","/fail.html").permitAll()// regexMatchers()通过正则表达式来匹配请求,// 第一个参数是请求方式,是一个enum类型,可以省略,省略后就是所有访问方法都可以访问// 不省略就是指定的访问方法才能访问.regexMatchers(HttpMethod.GET,".+[.]jpg").permitAll()// 表示这个请求时拥有root这个权限的才能访问,权限名严格区分大小写.antMatchers("/manager/**").hasAuthority("root")// 表示这个请求需要admin或者root的权限才能进入,参数是可变长参数.antMatchers("/user/**").hasAnyAuthority("admin","root")// 任何请求都需要认证.anyRequest().authenticated();// 没有权限,自动默认的登录请求http.formLogin();
}// 6种内置的授权访问方法
/*// 所有人都可以访问static final String permitAll = "permitAll";// 所有人都不允许访问private static final String denyAll = "denyAll";// 匿名访问,和permitAll()类似private static final String anonymous = "anonymous";// 所有请求需要认证后才能访问private static final String authenticated = "authenticated";// 全认证访问,只能通过输入账号和密码登录后才能访问,不能通过记住我来访问private static final String fullyAuthenticated = "fullyAuthenticated";// 通过记住我可以进行访问private static final String rememberMe = "rememberMe";*/

9、角色认证

​ 一个系统中拥有多种角色,需要根据不同的角色让其页面的展示效果不同,首先对登录的用户进行配置角色,然后判断角色能否访某个请求。

​ 在UserDetailsServiceImpl中授予用户角色。

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 1、 通过username从数据库中查询数据User user = new User(null, username, null);List<User> users = userMapper.queryUserByPojo(user);if (users.size()==0){// 如果不存在就抛出异常throw new UsernameNotFoundException("用户名不存在");}// 返回一个user对象,第一个参数是参数username,// 第二个参数是数据库中查询的密码,这个参数必须通过passwordEncoder加密,不然也不会通过,数据库保存的密码一般都是加密了的,不会是明文密码// 第三个参数权限列表,角色需要使用ROLE_开头表示这是一个角色,每个值之间使用逗号隔开return new org.springframework.security.core.userdetails.User(username,users.get(0).getPassword(),AuthorityUtils.commaSeparatedStringToAuthorityList("root,ROLE_用户"));
}

​ 请求角色的判断:

// 拦截所有的请求
http.authorizeRequests()// 为某些请求设置为所有人都可以访问.antMatchers("/","/index.html","/login.html","/rest.html","/assets/**","/fail.html").permitAll()// regexMatchers()通过正则表达式来匹配请求,// 第一个参数是请求方式,是一个enum类型,可以省略,省略后就是所有访问方法都可以访问// 不省略就是指定的访问方法才能访问.regexMatchers(HttpMethod.GET,".+[.]jpg").permitAll()//                // 表示这个请求时拥有root这个权限的才能访问,权限名严格区分大小写//                .antMatchers("/manager/**").hasAuthority("root")//                // 表示这个请求需要admin和root的权限才能进入,参数是可变长参数//                .antMatchers("/user/**").hasAnyAuthority("admin","root")// 表示这个请求需要拥有这个管理员角色才能访问,这是的角色不能加ROLE_为开头.antMatchers("/manager/**").hasRole("管理员")// 表示这个请求需要拥有这个管理员或者用户角色才能访问.antMatchers("/user/**").hasAnyRole("管理员","用户")// 任何请求都需要认证.anyRequest().authenticated();

10、记住我

springsecurity提供了记住我功能,通过简单的配置就能够实现。如果用户在登录的时候选择了记住我功能后,用户再次访问网站时springsecurity就帮我们从数据库中获取数据自动登录,所以需要使用到数据库的连接。

SecurityConfig配置类中,进行以下配置:

// 记住我的PersistentTokenRepository注入
@Bean
public PersistentTokenRepository getPersistentTokenRepository(){// 创建一个jdbcToken对象JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();// 设置数据源jdbcTokenRepository.setDataSource(druidDataSource);// 在第一次启动时创建一个表,用户存放数据,第二次启动时需要删除//        jdbcTokenRepository.setCreateTableOnStartup(true);return jdbcTokenRepository;
}

protected void configure(HttpSecurity http)这个方法中开启记住我功能:

// 记住我功能
http.rememberMe()// 设置前端的参数名,默认是remember-me.rememberMeParameter("remember")// 设置记住我功能的期限,默认是14天,单位是秒.tokenValiditySeconds(60*60*60)// 配置自定义登录逻辑.userDetailsService(userDetailsService)// 配置token.tokenRepository(this.getPersistentTokenRepository());

当每登录一次在数据库中新创建的表就会多一条记录:

image-20220115141057995

11、SpringSecurity整合thymeleaf

导入依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>

导入thymeleaf命名空间:

xmlns="http://www.w3.org/1999/xhtml"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"

11.1、获取登录信息

<div>登录账号:<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="details.remoteAddress"></span><br>sessionId:<span sec:authentication="details.sessionId"></span><br>
</div>

11.2、权限判断

<!-- 权限判断-->
<div>是否登录:<span sec:authorize="isAuthenticated()"></span><!-- 与权限设置内容类似--><!-- 权限判断 拥有root权限才会显示这个按钮--><button sec:authorize="hasAuthority('root')">删除</button><button sec:authorize="hasAnyAuthority('admin','root')">查看</button><!-- 角色判断 --><button sec:authorize="hasRole('超级管理员')">我是超级管理员</button><button sec:authorize="hasAnyRole('超级管理员','管理员')">我是超级管理员和管理员</button>
</div>

11.3、注销配置

// 退出功能
http.logout()// 设置退出的请求url,默认是/logout.logoutUrl("/user/logout")// 设置退出成功后的跳转页面.logoutSuccessUrl("/login.html");

The default is that accessing the URL “/logout” will log the user out by invalidating the HTTP Session, cleaning up any rememberMe() authentication that was configured, clearing the SecurityContextHolder, and then redirect to “/login?success”。可以请求/logout,然后就会清除session,然后重定向到/login?success

<li> <a th:href="@{/user/logout}"> <i class="fa fa-lock"></i> 退出系统 </a> </li>

12、csrf

CSRF(Cross-site request forgery),也被称为:one click attack/session riding,中文名称:跨站请求伪造,缩写为:CSRF/XSRF。

一般来说,攻击者通过伪造用户的浏览器的请求,向访问一个用户自己曾经认证访问过的网站发送出去,使目标网站接收并误以为是用户的真实操作而去执行命令。常用于盗取账号、转账、发送虚假消息等。攻击者利用网站对请求的验证漏洞而实现这样的攻击行为,网站能够确认请求来源于用户的浏览器,却不能验证请求是否源于用户的真实意愿下的操作行为。在springsecurity4以后就有了csrf防御,默认是开启的,但是在学习阶段都是关闭了csrf功能。

// 关闭csrf防御
http.csrf().disable();

springsecurity4以后为了防止crsf攻击,保证不是第三方网站访问,要求在访问时需要携带参数名为_csrf值为token的内容,token是服务器生成的。如果值和服务器的值得token匹配就允许访问。例如已登录为例,可以使用隐藏域来传入一个token值:

<!-- ${_csrf} 获取到服务器生成的token值-->
<input type="hidden" th:value="${_csrf}" name="_csrf" th:if="${_csrf}">

http://www.ppmy.cn/news/462561.html

相关文章

记一次ScriptEngine引发的Matespace内存泄漏

文章目录 前言一、发现问题二、问题分析三、解决问题总结 前言 内存泄漏我之前只遇到过堆内存泄漏和栈内存泄漏。matespace内存泄漏只在demo样例代码中见过。这次有幸遇到了&#xff0c;记录下留作纪念。 一、发现问题 在排查一个诡异的bug时突然灵光一闪想到会不会是GC导致的…

诛仙哪里炼器服务器最稳定,诛仙手游炼器方法 低成本稳定全身+11炼器攻略

诛仙手游中,炼器是游戏中最值得研究的地方,那么游戏中该怎么炼器,怎么低成本稳定+11呢,下面一起来看看吧。 一,炼器准备 首先炼器最重要的是心态,心态,心态!该上不要怂!不该上就得收手!太贪连上一般都是找死! 其次是技巧和运气 炼器需要具备以下几点 1、大量的炼器符 1~…

c语言程序模块化设计,C高级编程:基于模块化设计思想的C语言开发

C高级编程&#xff1a;基于模块化设计思想的C语言开发 作者&#xff1a;吉星 著 出版日期&#xff1a;2016年05月 文件大小&#xff1a;0.76M 支持设备&#xff1a; &#xffe5;60.00在线试读 适用客户端&#xff1a; 言商书局 iPad/iPhone客户端&#xff1a;下载 Android客户…

最佳托蒂徒劳 阿森纳吉星高照

北京时间3月12日凌晨&#xff0c;2008-09赛季欧洲冠军联赛1/8决赛次回合第二比赛日打响。客场作战的阿森纳队最终在点球大战中以7比6&#xff0c;总比分8比7的成绩淘汰罗马晋级8强。以下是双方球员评分。  罗马  多尼&#xff1a;6分。面对阿森纳&#xff0c;巴西门将的发挥…

小吉星

http://www.xjx.com.cn/ 来自 “ ITPUB博客 ” &#xff0c;链接&#xff1a;http://blog.itpub.net/79716/viewspace-838/&#xff0c;如需转载&#xff0c;请注明出处&#xff0c;否则将追究法律责任。 转载于:http://blog.itpub.net/79716/viewspace-838/

ShaderJoy —— 核心仅三行代码实现酷炫特效 “吉星高照 虎年鸿运” 【GLSL】

ShaderJoy —— Shader 特效乐趣无穷 效果图 动态图 静态图 核心代码分析 话不多说,先看核心代码,我们从简单的二维坐标入手(之后再推广到三维),如下 /// @note 将屏幕坐标换算为 [-.5, .5] 的范围 vec2 p = fragCoord.xy / iResolution.xy - .5

银号理财猛推期缴分成险 吉星高照名列力点

日前&#xff0c;招标银号五羊支行还举办了VIP存户会&#xff0c;特地推介新侨民寿的分成险吉庆相伴的晋级版——吉星高照。广东省安全事业协会的数据显现&#xff0c;中同胞寿正在外地市面120。4%。 与安全代理人出售的安全货物相比&#xff0c;银号安全货物的劣势正在于其设想…

吉星高照

18号一大早起床赶飞机前打开手机收到爸的短信&#xff0c;被告知订到28号返程的软卧车票&#xff0c;回了几条短信才想起他收不到我的短信。到家感受到兴奋之情绝对溢于言表的真正含义。早上五点起来打电话订票&#xff0c;天道酬勤&#xff01; 今年没有大年三十&#xff0c;…