1 - SpringSecurity 数据库设计
由于我没有学到密文处理,所以先使用可见密码
CREATE TABLE t_user(
id INT(11) NOT NULL AUTO_INCREMENT,
username VARCHAR(32) DEFAULT NULL,
`password` VARCHAR(255) DEFAULT NULL,
enabled TINYINT(1) DEFAULT NULL,
accountNotExpired TINYINT(1) DEFAULT NULL,
accountNonLocked TINYINT(1) DEFAULT NULL,
credentialsNonExpired TINYINT(1) DEFAULT NULL,
PRIMARY KEY (id)
)ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
INSERT INTO t_user(id,username,`password`,enabled,accountNotExpired,accountNonLocked,credentialsNonExpired)
VALUES(1,"root","{noop}123",1,1,1,1),(2,"admin","{noop}123",1,1,1,1),(3,"bir","{noop}123",1,1,1,1);
CREATE TABLE t_role (
id INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(32) DEFAULT NULL,
name_zh VARCHAR(32) DEFAULT NULL,
PRIMARY KEY(id)
)ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
INSERT INTO t_role (id ,`name` , name_zh)
VALUES(1,"ROLE_product","商品管理员"),(2,"ROLE_admin","系统管理员"),(3,"ROLE_user","用户管理员")
CREATE TABLE t_user_role (
id INT(11) NOT NULL AUTO_INCREMENT,
uid INT(11) DEFAULT NULL,
rid INT(11) DEFAULT NULL,
PRIMARY KEY(id),
KEY uid(uid),
KEY rid(rid)
)ENGINE=INNODB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
2 - pom文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<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>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.17</version>
</dependency>
</dependencies>
3 - 实体类映射 用户 和 角色信息
用户信息
public class User implements UserDetails {
private Integer id;
private String username;
private String password;
private Boolean enabled;
private Boolean accountNotExpired;
private Boolean accountNonLocked;
private Boolean credentialsNonExpired;
private List<Role> roles;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<SimpleGrantedAuthority> authorities = new HashSet<>();
roles.forEach((role) -> {
authorities.add(new SimpleGrantedAuthority(role.getName()));
});
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return accountNotExpired;
}
@Override
public boolean isAccountNonLocked() {
return accountNonLocked;
}
@Override
public boolean isCredentialsNonExpired() {
return credentialsNonExpired;
}
@Override
public boolean isEnabled() {
return enabled;
}
public void setId(Integer id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public void setAccountNotExpired(Boolean accountNotExpired) {
this.accountNotExpired = accountNotExpired;
}
public void setAccountNonLocked(Boolean accountNonLocked) {
this.accountNonLocked = accountNonLocked;
}
public void setCredentialsNonExpired(Boolean credentialsNonExpired) {
this.credentialsNonExpired = credentialsNonExpired;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public Integer getId() {
return id;
}
public List<Role> getRoles() {
return roles;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", enabled=" + enabled +
", accountNotExpired=" + accountNotExpired +
", accountNonLocked=" + accountNonLocked +
", credentialsNonExpired=" + credentialsNonExpired +
", roles=" + roles +
'}';
}
}
角色信息
@Data
@AllArgsConstructor
@ToString
@NoArgsConstructor
@Accessors(chain = true)
public class Role {
private Integer id;
private String name;
private String nameZh;
}
4 - 获取数据库信息
sql映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kaiguo.dao.UserDao">
<resultMap id="query" type="com.kaiguo.bean.User">
<id property="id" column="id" />
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="enabled" column="enabled" />
<result property="accountNotExpired" column="accountNotExpired" />
<result property="accountNonLocked" column="accountNonLocked" />
<result property="credentialsNonExpired" column="credentialsNonExpired" />
<collection property="roles" ofType="com.kaiguo.bean.Role">
<id property="id" column="rid"/>
<result property="name" column="name"/>
<result property="nameZh" column="name_zh"/>
</collection>
</resultMap>
<sql id="sqlChange" >
u.id id, u.username username, u.password, u.enabled enabled,u.accountNotExpired accountNotExpired,
u.accountNonLocked accountNonLocked ,u.credentialsNonExpired credentialsNonExpired,
r.id rid, r.name `name`, r.name_zh name_zh
</sql>
<select id="getUserAndRolesById" resultMap="query" parameterType="int" >
SELECT <include refid="sqlChange"/>
FROM t_user u
LEFT JOIN t_user_role ur
ON u.id = ur.uid
LEFT JOIN t_role r
ON ur.rid = r.id
where u.id = #{id}
</select>
<select id="getUserAndRolesByUsername" resultMap="query" parameterType="string" >
SELECT
<include refid="sqlChange"/>
FROM t_user u
LEFT JOIN t_user_role ur
ON u.id = ur.uid
LEFT JOIN t_role r
ON ur.rid = r.id
where u.username = #{username}
</select>
</mapper>
application.yml
server:
port: 8081
spring:
thymeleaf:
cache: false
datasource:
url: jdbc:mysql://localhost:3306/housedb
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
mybatis:
mapper-locations: mapper/*.xml
dao接口
public interface UserDao {
public User getUserAndRolesById(Integer id);
public User getUserAndRolesByUsername(String username);
}
service层
public interface UserService {
public User getUserAndRolesById(Integer id);
public User getUserAndRolesByUsername(String username);
}
service 实现层
@Service
public class UserServiceImpl implements UserService {
private UserDao userDao;
@Autowired
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
@Override
public User getUserAndRolesById(Integer id) {
return userDao.getUserAndRolesById(id);
}
@Override
public User getUserAndRolesByUsername(String username) {
return userDao.getUserAndRolesByUsername(username);
}
}
5 - 改变SpringSecurity的验证策略
修改SpringSecurity验证策略:
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private MyUserDetailService myUserDetailService;
@Autowired
public SecurityConfiguration(MyUserDetailService myUserDetailService) {
this.myUserDetailService = myUserDetailService;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailService);
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public LoginFilter loginFilter() throws Exception {
LoginFilter loginFilter = new LoginFilter();
loginFilter.setFilterProcessesUrl("/doLogin");
loginFilter.setUsernameParameter("username");
loginFilter.setPasswordParameter("password");
loginFilter.setAuthenticationManager(authenticationManagerBean());
loginFilter.setAuthenticationSuccessHandler(((request, response, authentication) -> {
Map<String, Object> result = new HashMap<>();
result.put("message", "登录成功");
result.put("user", authentication.getPrincipal());
result.put("status", 201);
response.setStatus(HttpStatus.OK.value());
response.setContentType("application/json;charset=utf-8");
response.getWriter().print(new ObjectMapper().writeValueAsString(result));
}));
loginFilter.setAuthenticationFailureHandler((request, response, exception) -> {
Map<String, Object> result = new HashMap<>();
result.put("message", "登录失败," + exception.getMessage());
result.put("status", 400);
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.setContentType("application/json;charset=utf-8");
response.getWriter().print(new ObjectMapper().writeValueAsString(result));
});
return loginFilter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.csrf().disable();
http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
自定义验证策略
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws
AuthenticationException {
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
if (request.getContentType().equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE)) {
// 提取json
try {
Map<String, String> userInfo = new ObjectMapper().readValue(request.getInputStream(), Map.class);
String username = userInfo.get(this.getUsernameParameter());
String password = userInfo.get(this.getPasswordParameter());
System.out.println(userInfo);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
setDetails(request, token);
return this.getAuthenticationManager().authenticate(token);
} catch (IOException e) {
e.printStackTrace();
}
}
return super.attemptAuthentication(request, response);
}
}
验证数据库信息
@Component
public class MyUserDetailService implements UserDetailsService {
private UserService userService;
@Autowired
public MyUserDetailService(UserService userService) {
this.userService = userService;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User userdb = userService.getUserAndRolesByUsername(username);
if (userdb == null)
throw new UsernameNotFoundException("用户名不正确");
System.out.println("可以");
return userdb;
}
}
6 - 测试
入口类
@SpringBootApplication
@MapperScan("com.kaiguo.dao")
public class SecurityApplication {
public static void main(String[] args) {
SpringApplication.run(SecurityApplication.class,args);
}
}
postman测试: 测试正确的用户名和密码
postman测试: 测试正确的用户和错误的密码
7 - 注意
由于没用使用到jwt的缘故,在ajax 请求一定要携带浏览器的jsessionId,用于验证当前浏览器是否成功登录过!,后期会使用jwt