目录
前置知识(可略):
关于表(可略)
Bean
实现UserDetailsService
配置SecurityConfig
test
踩坑
springboot(搭个架子,连接好jdbc,跑得通maping)
数据库关系映射(mybaitis+mysql+表结构)
security引入(pom文件)
项目目录结构
ps:密码保存数据库先加密
加密示范
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
String encode = bCryptPasswordEncoder.encode("123");
System.out.println(encode);
建表
CREATE TABLE `learn_security_per_role` (
`role_id` int(11) DEFAULT NULL,
`perm_id` int(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE `learn_security_permission` (
`permid` int(11) NOT NULL,
`permTag` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`permName` varchar(255) COLLATE utf8_bin DEFAULT NULL,
PRIMARY KEY (`permid`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE `learn_security_role` (
`roleid` int(11) DEFAULT NULL,
`rolename` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`roledesc` varchar(255) COLLATE utf8_bin DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE `learn_security_user` (
`userid` int(255) NOT NULL AUTO_INCREMENT,
`username` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`userrealname` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`password` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`createdate` datetime DEFAULT NULL,
`lastlogintime` datetime DEFAULT NULL,
`isavailable` int(255) DEFAULT NULL,
`isexpired` int(255) DEFAULT NULL,
`islock` int(255) DEFAULT NULL,
`identifyIsExpired` int(255) DEFAULT NULL,
PRIMARY KEY (`userid`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE `learn_security_user_role` (
`user_id` int(11) DEFAULT NULL,
`role_id` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
(根据表把bean都写好,只给出user,user很重要,其他自建)
解释:此类会直接绑定security自带的login页面,也就是通过表单提交用户名密码,重写UserDetails 的几个方法,是为了给security自带的各个功能比如isLock一类,会自动关联数据库(如果你重写的话,重写记得要修改代码)
GrantedAuthority会绑定hasAnyAuthority,也要写
package com.yiki.blog.SecurityLearn;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
public class User implements UserDetails {
private Integer userID;
private String userName;
private String userRealName;
private String passWord;
private Date creatDate;
private Date lastLoginTime;
private boolean isAvailable;//是否可用
private boolean isExpired;//是否过期
private boolean isLock;//是否锁定
private boolean identifyIsExpired;//证书是否过期
private List authorityList = new ArrayList();
public void setAuthorities(List authorityList) {
this.authorityList = authorityList;
}
@Override
public Collection extends GrantedAuthority> getAuthorities() {
return authorityList;
}
@Override
public String getPassword() {
return passWord;
}
@Override
public String getUsername() {
return userName;
}
@Override
public boolean isAccountNonExpired() {
return isExpired;
}
@Override
public boolean isAccountNonLocked() {
return isLock;
}
@Override
public boolean isCredentialsNonExpired() {
return identifyIsExpired;
}
@Override
public boolean isEnabled() {
return isAvailable;
}
public Integer getUserID() {
return userID;
}
public void setUserID(Integer userID) {
this.userID = userID;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserRealName() {
return userRealName;
}
public void setUserRealName(String userRealName) {
this.userRealName = userRealName;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
public Date getCreatDate() {
return creatDate;
}
public void setCreatDate(Date creatDate) {
this.creatDate = creatDate;
}
public Date getLastLoginTime() {
return lastLoginTime;
}
public void setLastLoginTime(Date lastLoginTime) {
this.lastLoginTime = lastLoginTime;
}
public boolean isAvailable() {
return isAvailable;
}
public void setAvailable(boolean available) {
isAvailable = available;
}
public boolean isExpired() {
return isExpired;
}
public void setExpired(boolean expired) {
isExpired = expired;
}
public boolean isLock() {
return isLock;
}
@Override
public String toString() {
return "User{" +
"userID=" + userID +
", userName='" + userName + '\'' +
", userRealName='" + userRealName + '\'' +
", passWord='" + passWord + '\'' +
", creatDate=" + creatDate +
", lastLoginTime=" + lastLoginTime +
", isAvailable=" + isAvailable +
", isExpired=" + isExpired +
", isLock=" + isLock +
", identifyIsExpired=" + identifyIsExpired +
", authorityList=" + authorityList +
'}';
}
public void setLock(boolean lock) {
isLock = lock;
}
public boolean isIdentifyIsExpired() {
return identifyIsExpired;
}
public void setIdentifyIsExpired(boolean identifyIsExpired) {
this.identifyIsExpired = identifyIsExpired;
}
}
Mapper
package com.yiki.blog.SecurityLearn;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface UserMapper {
public User getUserByUserName(String name);
//查询用户拥有的权限
public List getPermissionByUserName(String name);
}
在这里写了会自动绑定login页面(自带那个)
package com.yiki.blog.SecurityLearn;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.ArrayList;
import java.util.List;
//security提供的自定义类的用户信息巴拉巴拉(可构造)
//UserDetails 接口
@Service
public class YikiUserDetailService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userMapper.getUserByUserName(username);
List permList = userMapper.getPermissionByUserName(username);
List grantedAuthorities = new ArrayList<>();
for (Permission perm : permList) {
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(perm.getPermName());
grantedAuthorities.add(grantedAuthority);
}
user.setAuthorities(grantedAuthorities);
return user;
}
}
权限参考
package com.yiki.blog.SecurityLearn;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
//1配置类
@Configuration
@EnableWebSecurity//2启动security过滤器链
//3继承这个配置类
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//4.重写这两个方法
//7.在登录页用封装好的userdetail
@Autowired
private YikiUserDetailService userDetailService;
/**
* 5-1代替xml配置的AuthenticationManager(认证管理器)
* 认证的信息获取
*/
@Override//
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
/*6.2.A
* 写死的配置---~硬编码
* There is no PasswordEncoder mapped for the id “null”
* 时过境迁,psw报错id为null是因为security默认会对密码解密方式需要定义,也就是{id},若没有则为null,则会报错
* 记得在设置密码给个加密方式 *
auth.inMemoryAuthentication()
.passwordEncoder(new BCryptPasswordEncoder())
.withUser("Admin")
.password(new BCryptPasswordEncoder().encode("123456"))
.authorities("Auth");//可以用,追加权限
*/
/*
* 6.2 B自定义service类
* */
auth.userDetailsService(userDetailService).passwordEncoder(new BCryptPasswordEncoder());;
}
/**
* 5-2.代替之前标签配置
* 需要拦截资源/角色权限/登录方式:httpBasic,FormLogin)
* spring拦截到的http会转发到这里
* 其他:/**全部路径,/*一级路径
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
/*6-1拦截表达式A.httpBasic
http.authorizeRequests()
.antMatchers("/**")
.fullyAuthenticated()
.and()
.httpBasic();
@isAuthenticated()
Returns true if the user is not anonymous
@isFullyAuthenticated()
Returns true if the user is not an anonymous or a remember-me user
@anonymous()
只有匿名用户可以访问资源,登录后不允许访问
* */
/*B.formLogin
* */
http.authorizeRequests()
.antMatchers("/security/*").hasAnyAuthority("ROLE_SEARCH,ROLE_DELETE")
.and().csrf().disable()//禁止自带跨域
.formLogin();
}
}
package com.yiki.blog.SecurityLearn;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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;
import java.util.ArrayList;
import java.util.List;
@ResponseBody
@Controller
@RequestMapping("/security")
public class TestController {
@Autowired
UserMapper userMapper;
@GetMapping("/name1")
public User index1() {
User user = new User();
user = userMapper.getUserByUserName("yiki");
List permList = userMapper.getPermissionByUserName("yiki");
List grantedAuthorities = new ArrayList<>();
for (Permission perm : permList) {
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(perm.getPermTag());
grantedAuthorities.add(grantedAuthority);
}
user.setAuthorities(grantedAuthorities);
System.out.println(user);
return user;
}
@GetMapping("/name2")
public List name2() {
return userMapper.getPermissionByUserName("yiki");
}
}
1. Empty encoded password
user类重写的方法里password不能返回null,而是
@Override
public String getPassword() {
return passWord;
}
2.There is no PasswordEncoder mapped for the id "null"
这个就是密码前{id}xxx是指告诉框架加密方式
硬编码处
auth.inMemoryAuthentication()
.passwordEncoder(new BCryptPasswordEncoder())
.withUser("Admin")
.password(new BCryptPasswordEncoder().encode("123456"))
.authorities("Auth");//可以用,追加权限