Spring Security 安全框架 (一) 基础操作
创始人
2024-01-21 07:05:42

1.password 登录密码

在 springboot 项目中 , 引入依赖

        org.springframework.bootspring-boot-starter-securityorg.springframework.bootspring-boot-starter-web

启动时

在这里插入图片描述

1.1.默认密码

启动时 , 控制台可以看到 生成一个UUID 作为 密码

Using generated security password: 876205ea-25bd-47b2-9c68-e2ac52377915

用户名为 user

1.2. 配置文件配置密码

在 application.properties 配置文件 中加入

# 设置 用户名密码
spring.security.user.name=admin
spring.security.user.password=123

1.3.密码生成器

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;/*** security 配置文件*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {/*** 密码生成器* @return*/@BeanPasswordEncoder passwordEncoder() {// 无加密密码return NoOpPasswordEncoder.getInstance();}/*** 设置 用户 密码 及 角色* @param auth* @throws Exception*/@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication().withUser("yuan@123").password("123").roles("admin");}}

2.登录页面

 /*** 配置忽略掉的 URL 地址,一般对于静态文件* @param web* @throws Exception*/@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/js/**", "/css/**","/img/**","/font/**");}/***  请求属性配置* @param http* @throws Exception*/@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().anyRequest().authenticated().and().formLogin().loginPage("/login.html")  //转向登录页面.loginProcessingUrl("/doLogin")  // 登录请求.usernameParameter("username")   // 账号标识.passwordParameter("password")   // 密码标识//.successForwardUrl("/success")  // 登录成功跳转(内部转, 登录成功跳转到指定请求).defaultSuccessUrl("/success")    // 登录成功跳转(重定向, 登录成功就回到之前访问的资源).failureForwardUrl("/login.html").failureUrl("/login.html").permitAll().and().logout()//.logoutUrl("/logout")  // GET方式 调用logout.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "POST"))  // POST 方式调用 logout.logoutSuccessUrl("/login.html")  // 退出转向.invalidateHttpSession(true)  // 清空session.clearAuthentication(true)    // 清空认证信息.permitAll().and().csrf().disable();}
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class HelloController {@RequestMapping("/hello")public String sayHello(){return "hello";}@RequestMapping("/success")public String success(){return "success";}
}

3.返回信息

3.1.登录成功返回

//.successForwardUrl("/success")//.defaultSuccessUrl("/success").successHandler((request,response, authentication)->{response.setContentType("application/json;charset=utf-8");PrintWriter out = response.getWriter();out.write(new ObjectMapper().writeValueAsString(authentication.getPrincipal()));out.flush();out.close();})
{"password": null,"username": "yuan@123","authorities": [{"authority": "ROLE_admin"}],"accountNonExpired": true,"accountNonLocked": true,"credentialsNonExpired": true,"enabled": true
}

3.2.登录失败返回

//.failureForwardUrl("/login.html")//.failureUrl("/login.html").failureHandler((request,response, exception)->{response.setContentType("application/json;charset=utf-8");PrintWriter out = response.getWriter();String msg = "";if (exception instanceof LockedException) {msg = "账户被锁定,请联系管理员!";} else if (exception instanceof CredentialsExpiredException) {msg = "密码过期,请联系管理员!";} else if (exception instanceof AccountExpiredException) {msg = "账户过期,请联系管理员!";} else if (exception instanceof DisabledException) {msg = "账户被禁用,请联系管理员!";} else if (exception instanceof BadCredentialsException) {msg = "用户名或者密码输入错误,请重新输入!";}out.write(new ObjectMapper().writeValueAsString(msg));out.flush();out.close();})

3.3.未登录请求

.exceptionHandling()
.authenticationEntryPoint((req, resp, authException) -> {resp.setContentType("application/json;charset=utf-8");PrintWriter out = resp.getWriter();out.write("尚未登录,请先登录");out.flush();out.close();}
)

3.4.登出注销

 .logout().logoutUrl("/logout")//.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "POST"))//.logoutSuccessUrl("/login.html")//.invalidateHttpSession(true)//.clearAuthentication(true).logoutSuccessHandler((req, resp, authentication) -> {resp.setContentType("application/json;charset=utf-8");PrintWriter out = resp.getWriter();out.write("注销成功");out.flush();out.close();})

4.角色授权

4.1.设置账号

将原来方法注释, 使用新的方法

 ///**// * 设置 用户 密码 及 角色// * @param auth// * @throws Exception// *///@Override//protected void configure(AuthenticationManagerBuilder auth) throws Exception {//    auth.inMemoryAuthentication()//            .withUser("yuan")//            .password("123")//            .roles("admin");//}/*** pring Security 支持多种数据源,例如内存、数据库、LDAP 等,* 这些不同来源的数据被共同封装成了一个 UserDetailService 接口,* 任何实现了该接口的对象都可以作为认证数据源。* @return*/@Override@Beanprotected UserDetailsService userDetailsService() {// 在内存中存储, 创建两个账号 , 分别赋 admin 和  user 权限InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();manager.createUser(User.withUsername("admin").password("123").roles("admin").build());manager.createUser(User.withUsername("yuan").password("123").roles("user").build());return manager;}

4.2.增加响应方法

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class HelloController {/*** 只要登录就只可以访问* @return*/@RequestMapping("/hello")public String sayHello(){return "hello";}/*** 只有 admin 角色才能访问* @return*/@GetMapping("/admin/hello")public String admin() {return "admin";}/***  admin,  user 角色都可以访问* @return*/@GetMapping("/user/hello")public String user() {return "user";}
}

4.3.设置角色权限

 /***  请求属性配置* @param http* @throws Exception*/@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/admin/**").hasRole("admin").antMatchers("/user/**").hasRole("user").anyRequest().authenticated().and()...

将使用 admin 访问 /user/hello 时会报错

{"timestamp": "2021-11-05T14:27:39.537+00:00","status": 403,"error": "Forbidden","message": "","path": "/user/hello"
}

4.4.角色继承

    /*** 角色继承* @return*/@BeanRoleHierarchy roleHierarchy() {RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();hierarchy.setHierarchy("ROLE_admin > ROLE_user");return hierarchy;}

这样 使用 admin 访问 /user/hello 就可以了

5.访问数据库

5.1.数据库

5.2.实体类

实现 UserDetails 接口 , 覆盖对应的方法


import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;import lombok.Data;
import org.apache.ibatis.mapping.FetchType;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;/*** * @TableName sys_user*/
@TableName(value ="sys_user")
@Data
public class UserEntity implements UserDetails, Serializable {/*** */@TableId(type = IdType.AUTO)private Integer userId;/*** */private String userName;/*** */private String userPass;/*** */private String salt;/*** */private String nickName;@TableField(exist = false)private static final long serialVersionUID = 1L;// 账户是否没有过期@TableField(exist = false)private boolean accountNonExpired = true;//账户是否没有被锁定@TableField(exist = false)private boolean accountNonLocked = true;//密码是否没有过期@TableField(exist = false)private boolean credentialsNonExpired = true;//账户是否可用@TableField(exist = false)private boolean enabled = true;@TableField(exist = false)private List roles;// 返回用户的角色信息@Overridepublic Collection getAuthorities() {List authorities = new ArrayList<>();for (RoleEntity role : getRoles()) {// 注意这里的 角色 前缀authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleCode()));}return authorities;}@Overridepublic String getPassword() {return this.userPass;}@Overridepublic String getUsername() {return this.userName;}@Overridepublic boolean isAccountNonExpired() {return accountNonExpired;}@Overridepublic boolean isAccountNonLocked() {return accountNonLocked;}@Overridepublic boolean isCredentialsNonExpired() {return credentialsNonExpired;}@Overridepublic boolean isEnabled() {return enabled;}
}

5.3.service 查询


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
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.Service;import java.util.List;/****/
@Service
public class UserServiceImpl extends ServiceImpl
implements UserService, UserDetailsService {@Overridepublic UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {UserEntity user = this.getOne(new LambdaQueryWrapper().eq(UserEntity::getUsername, name));if (user == null) {throw new UsernameNotFoundException("用户不存在");}user.setRoles(this.getRolesByUserId(user.getUserId()));System.out.println("user = " + user);return user;}public List getRolesByUserId(Integer userId){// 通过 数据库 连表 , 根据 用户id 查询对应的 role 集合return this.baseMapper.selectRoleListByUserId(userId);}
}

5.4.security配置类

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.authentication.*;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;import java.io.PrintWriter;/*** security 配置类*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate UserServiceImpl userService;/*** 密码生成器* @return*/@BeanPasswordEncoder passwordEncoder() {// 自带加密器BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();return bCryptPasswordEncoder;}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userService).passwordEncoder(passwordEncoder());}/*** 角色继承* @return*/@BeanRoleHierarchy roleHierarchy() {RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();hierarchy.setHierarchy("ROLE_admin > ROLE_user");return hierarchy;}/*** 配置忽略掉的 URL 地址,一般对于静态文件* @param web* @throws Exception*/@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/js/**", "/css/**","/img/**","/font/**");}/***  请求属性配置* @param http* @throws Exception*/@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/admin/**").hasRole("admin").antMatchers("/user/**").hasRole("user").anyRequest().authenticated().and().formLogin().loginProcessingUrl("/doLogin").successHandler((request,response, authentication)->{response.setContentType("application/json;charset=utf-8");PrintWriter out = response.getWriter();out.write(new ObjectMapper().writeValueAsString(authentication.getPrincipal()));out.flush();out.close();}).failureHandler((request,response, exception)->{response.setContentType("application/json;charset=utf-8");PrintWriter out = response.getWriter();String msg = "";if (exception instanceof LockedException) {msg = "账户被锁定,请联系管理员!";} else if (exception instanceof CredentialsExpiredException) {msg = "密码过期,请联系管理员!";} else if (exception instanceof AccountExpiredException) {msg = "账户过期,请联系管理员!";} else if (exception instanceof DisabledException) {msg = "账户被禁用,请联系管理员!";} else if (exception instanceof BadCredentialsException) {msg = "用户名或者密码输入错误,请重新输入!";}out.write(new ObjectMapper().writeValueAsString(msg));out.flush();out.close();}).permitAll().and().logout().logoutUrl("/logout").logoutSuccessHandler((req, resp, authentication) -> {resp.setContentType("application/json;charset=utf-8");PrintWriter out = resp.getWriter();out.write("注销成功");out.flush();out.close();}).and().csrf().disable().exceptionHandling().authenticationEntryPoint((req, resp, authException) -> {resp.setContentType("application/json;charset=utf-8");PrintWriter out = resp.getWriter();out.write("尚未登录,请先登录");out.flush();out.close();});}}

相关内容

热门资讯

长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
不用扬鞭自奋蹄 乘势而上千帆竞... 思明区正全力向“两个进位”目标发起冲刺,努力为建设高素质高颜值现代化国际化的厦门作出“思明贡献”。(...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
好听的民谣歌曲 2021最火的... #我的年度歌单#在2021年里听过很多民谣,但是最能触及心中温柔的还是下面的几首。不听民谣的理由有很...