书接上一篇,在实际的开发中,我们的账号密码不可能是这样写在配置文件中的,应该是要来自于数据库。
接着上一篇的项目,我们继续,在原有的依赖的基础上新增,mysql驱动依赖和mybatis依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>3.0.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
在数据库新建一张用户表
我们简单一点,就4个字段
添加一条数据,在security中,你的密码默认是加密的,不加密在密码前加(noop)
现在我们要用这个账号密码去登录,我们一步一步来操作一下。
先配置一下数据库配置:
然后我们需要一个实体类
user实现UserDetails 接口,重写里面的方法
package com.example.demo.model;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
/**
* 定义用户对象,需要实现UserDetails 接口,对于Spring Security框架而言,所有的用户对象都是一个UserDetails 的实例
*
* 根据自己实际情况,实现接口里的方法
*/
public class User implements UserDetails {
private Integer id;
private String username;
private String password;
private Boolean enabled;
public Integer getId() {
return id;
}
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;
}
/**
* 用来放回当前用户的角色/权限信息
* @return
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
/**
* 获取用户的密码
* @return
*/
@Override
public String getPassword() {
return password;
}
/**
* 获取用户的用户名
* @return
*/
@Override
public String getUsername() {
return username;
}
/**
* 账户是否没有过期(true是未过期,false是过期)
* @return
*/
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* 账户是否没有被锁定(true是未被锁定,false是已锁定)
* @return
*/
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* 密码是否没有过期
* 一些公司邮件系统密码要求半年修改一次,不改就无法登录
* @return
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* 账户是否可用
* @return
*/
@Override
public boolean isEnabled() {
return enabled;
}
}
我们的user对象就已经写好了
接下来写一个userservice,
同样这个接口也要实现一个UserDetailsService接口
代码如下
package com.example.demo.service;
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;
@Service
public class UserService implements UserDetailsService {
/**
* 根据用户名查询用户对象
* @param username //用户登录时输入的用户名
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return null;
}
}
然后来一个mapper,
将UserMapper 注入到UserService中
package com.example.demo.service;
import com.example.demo.mapper.UserMapper;
import com.example.demo.model.User;
import org.springframework.beans.factory.annotation.Autowired;
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;
@Service
public class UserService implements UserDetailsService {
@Autowired
UserMapper userMapper;
/**
* 根据用户名查询用户对象
* @param username //用户登录时输入的用户名
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User u = userMapper.loadUserByUsername(username);
if (u == null){
//说明用户名不存在
throw new UsernameNotFoundException("账号不存在");
}
return u;
}
}
UserMapper如下
package com.example.demo.mapper;
import com.example.demo.model.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper {
User loadUserByUsername(String username);
}
UserMapper.xml如下
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/schema/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
<select id="loadUserByUsername" resultType="com.example.demo.model.User">
select * from user where username=#{username};
</select>
</mapper>
最后在pom文件的build里加
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
运行成功
输入账号,密码登录,运行成功。
这里报404,是因为没有这个页面。
附带说一下
如果是boot2.x版本,5.x的security会出现一个
java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"
的错误
这个时候新建两个类就行了
package org.ikun.security_demo;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
@Component
public class MyPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
return rawPassword.toString();
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return encodedPassword.equals(rawPassword.toString());
}
}
package org.ikun.security_demo;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
//设置登录,注销,表单登录不用拦截,其他请求要拦截
http.authorizeRequests().antMatchers("/").permitAll()
.anyRequest().authenticated()
.and()
.logout().permitAll()
.and()
.formLogin();
//关闭默认的csrf认证
http.csrf().disable();
}
@Override
public void configure(WebSecurity web) throws Exception {
//设置静态资源不要拦截
web.ignoring().antMatchers("/js/**","/css/**","/images/**");
}
}