书接上一篇,在实际的开发中,我们的账号密码不可能是这样写在配置文件中的,应该是要来自于数据库。
接着上一篇的项目,我们继续,在原有的依赖的基础上新增,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/**"); } }