权限目前最常用的有两种,一种就是 Apache Shiro ,另一种是 Spring Security。其实两种权限框架都是大同小异,会其中一种就很好学习另外一种;今天就讲解一下 Spring 的 Security;
首先说一下常见的表关系
一对一
如果两张表存在一对一的关系,即A表 name,pic ,mail,age 中的一条数据最多只对应B表(公司,住址,户籍(频率低),详情介绍(大))中的一条数据,反之亦然。此时完全可以设计成一张表。
即使可以合成一张表,在有些公司中还是有分成两张表的情况。
一对多
学校与班级就是一对多的关系 多的一方使用外键维护关系
大学中,学生与课程的关系就是多对多,一个学生可以正在学习多门课程,一门课程也可以有多个学生正在学习。
多对多
大学中,学生与课程的关系就是多对多,一个学生可以正在学习多门课程,一门课程也可以有多个学生正在学习。
准备数据库表
- 用户表
- 角色表
- 用户_角色 中间表
- 权限表
- 角色_权限 中间表
初步接触
1.导包
<!-- 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>
2.准备 Controller
@Controller
public class LoginController extends BaseController {
@RequestMapping("/index")
@ResponseBody
public String indexLogin(){
return "老蔫~关注一波!";
}
3.配置yml
spring:
security:
user:
name: laonian
password: 123456
4.访问测试 (直接 访问 我这里是 http://localhost/login)
这个登录密码是自带的
这 就是简易演示登录效果,就是你导包,security 会自动装配 ,但是现实项目中 肯定不会这么搞,不可能在 yml 配置账户密码,也可以存在内存中在配置类中搭配账户密码 ,但也有意义,写在代码里 没啥卵用;所以要修改
正式版(从数据库读取用户信息)
1.pom.xml
<!-- security 权限包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
<!--数据库驱动,可根据自己需要自行删减-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector-java.version}</version>
</dependency>
<!--spring boot依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2.到了mybatis 就要搭配数据源 yml
# Mysql数据库
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/user?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezone=CTT
username: root
password: 121212
filters: wall,mergeStat
data-source-name: gunsdb
mybatis-plus:
configuration:
map-underscore-to-camel-case: true # 是否开启自动驼峰命名
# xml 扫描 可用逗号或斜杠 分隔(告诉Mapper 所对应的XML文件位置)
mapper-locations: classpath:cn/zx/modular/mapper/xml/*.xml,classpath:cn/zx/modular/mapper/xml/*.xml
global-config:
db-config:
id-type: auto # 主键类型 AUTO:数据库ID自增
type-aliases-package: cn.zx.modular.entity #MyBaits 别名包扫描路径,通过该属性可以给包中的类注册别名
3.启动类
/**
* SpringBoot启动类
*
* @SpringBootApplication 自动扫描 cn.zx 下的所有包 (package cn.zx)
* @Componentscan 指定扫描
* @EnableAutoConfiguration 开启自动扫描
*/
@SpringBootApplication(exclude = {WebAutoConfiguration.class})
@EnableScheduling
@EnableCaching
public class GunsApplication {
private final static Logger logger = LoggerFactory.getLogger(GunsApplication.class);
/**
* 启动方法
*/
public static void main(String[] args) {
SpringApplication.run(GunsApplication.class,args);
logger.info(GunsApplication.class.getSimpleName() " is success !");
}
}
4.用户实体类
/**
* 用户表
*
* easypoi excel 操作
* @Excel: 代表这个字段要生成到excel中去
* @name: 这个excel的表头名称
* @width: 这一列的宽度设置
*
*
* Serializable
* Serializable接口是启用其序列化功能的接口。实现java.io.Serializable 接口的类是可序列化的。没有实现此接口的类将不能使它们的任意状态被序列化或逆序列化。
* 1、存储对象在存储介质中,以便在下次使用的时候,可以很快捷的重建一个副本;2、便于数据传输,尤其是在远程调用的时候。
* UserDetails
* security 核心包里的,不管你用户名字段叫什么,只管你在getUsername把你的用户名字段返回去。去掉差异化,
*/
@Data
@TableName("user")
public class UserPo implements Serializable , UserDetails {
/** 用户ID */
private Long id;
/** 账户 */
private String userName;
/** 用户密码 */
private String password;
/** 盐值 */
private String salt;
@Excel(name = "用户名称")
private String name;
@Excel(name = "电话号",width = 20)
private String phone;
@Excel(name = "邮件",width = 20)
private String email;
/** type 1-文本 2-图片 3-函数 10-数字 默认文本 */
@Excel(name = "头像",type = 2,width = 20)
private String portrait;
/** 时间格式 */
@Excel(name = "生日",format = "yyyy-MM-dd")
private String birthday;
/** true - 男 false - 女 */
@Excel(name = "性别",replace = {"男_true","女_false"})
private Boolean sex;
/** 1-启用 2-禁用 3-违规 */
@Excel(name = "账号状态")
private int status;
/** 一对多的情况 比如部门等 */
@ExcelEntity
private Level level;
/** 创建时间 */
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT 8")
private String crateDate;
/**
* <? extends GrantedAuthority> 表示 Collection 装GrantedAuthority子类
* <? super GrantedAuthority> 表示 Collection 装GrantedAuthority父类
* */
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
/** 获取用户名 */
@Override
public String getUsername() {
return this.userName;
}
/** 看账号是否未过期 */
@Override
public boolean isAccountNonExpired() {
return true;
}
/** 是否未锁定*/
@Override
public boolean isAccountNonLocked() {
return true;
}
/** 证书未过期 */
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/** 是否被禁用*/
@Override
public boolean isEnabled() {
if (this.status != 1){
return false;
}
return true;
}
}
5.权限类
/**
* @author laonian
* @date 2021-12-30 13:27
* 菜单表
*/
@TableName("menu")
@Data
public class Menu implements GrantedAuthority , Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id",type = IdType.ID_WORKER)
private Long id;
@TableField("code")
private String code;
private String name;
private String status;
private Integer type;
private String uri;
private Date createTime;
private Integer sort;
@Override
public String getAuthority() {
return this.uri;
}
}
6.Mapper
@Mapper
public interface UserMapper extends BaseMapper<UserPo> {
/** 通过名字查询信息 */
UserPo getUserByName(String name);
/** 查询用户所有权限*/
List<Menu> getMenuByUserId(Long userId);
}
7.XML
<!-- 通过名字查询信息 -->
<select id="getUserByName" resultType="cn.zx.modular.entity.UserPo">
select * from user where user_name = #{name}
</select>
<!-- 查询用户所有权限 -->
<select id="getMenuByUserId" resultType="cn.zx.modular.entity.Menu">
SELECT * FROM menu m WHERE m.id in(
SELECT menu_id FROM menu_role WHERE status = 'ENABLE' AND role_id in(SELECT role_id FROM user_role WHERE user_id = #{userId} )
)
</select>
</mapper>
8.Service
public interface UserService extends IService<UserPo> {
UserPo getUserByName(String name);
List<Menu> getMenuByUserId(Long userId);
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserPo> implements UserService {
@Resource
private UserMapper userMapper;
@Override
public UserPo getUserByName(String name) {
UserPo userByName = userMapper.getUserByName(name);
//断言
Assert.isTrue(userByName!=null,"您输入的账户不存在");
return userByName;
}
@Override
public List<Menu> getMenuByUserId(Long userId) {
return userMapper.getMenuByUserId(userId);
}
}
9.自定义 Service 有用户的信息也有用户权限
/**
* @author laonian
* @date 2021-12-30 19:44
*/
@Service("userDetailsService")
public class MyUserDetailService implements UserDetailsService {
@Autowired
UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserPo user = userService.getUserByName(username);
List<Menu> menuByUserList = userService.getMenuByUserId(user.getId());
HashSet<Menu> menus = new HashSet<>(menuByUserList);
user.setAuthorities(menus);
return user;
}
}
10.重新定义 SecurityConfig
/**
* @author laonian
* @date 2021-12-30 19:34
*/
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/css/**", "/js/**", "/fonts/**").permitAll() //都可以访问
.anyRequest().authenticated() // 任何请求都需要认证
.and()
.formLogin() //基于Form表单登录验证
.and()
.userDetailsService(userDetailsService);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
11.Controller
@PreAuthorize("hasAuthority('/index')")
@RequestMapping("/index")
@ResponseBody
public String indexLogin(){
return "老蔫~关注一波!";
}
自己试效果吧 大概就算入门了
你的善良必须有点锋芒否则等于零留个关注 么么哒!!!,