前言
在企业开发中系统的认证和权限控制是常见的需求,前面几篇文章已经介绍了Spring Boot集成Shiro实现权限验证,本文将讲解Spring Boot集成Spring Security如何实现认证和权限控制。
简介
Spring Security是一种基于Spring AOP和过滤器Filte的框架,它提供了供在Web请求和方法调用级别的用户鉴权和权限控制。 Web应用的安全性通常包括:用户认证(Authentication)和用户授权(Authorization)。
- 用户授权:指的是验证某个用户是否为系统合法用户,也就是说用户能否访问该系统。
- 用户认证一般要求用户提供用户名和密码,系统通过校验用户名和密码来完成认证。
认证流程图
核心组件
SecurityContextHolder
用于存储应用程序安全上下文(Spring Context)的详细信息,如当前操作的用户对象信息、认证状态、角色权限信息
获取用户信息:
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails)
{
String username = ((UserDetails)principal).getUsername();
}
else
{
String username = principal.toString();
}
Authentication
认证信息接口,集成了Principal类返回用户和权限信息,该接口提供如下方法:
AuthenticationManager
认证管理器负责验证,认证成功后,AuthenticationManager返回用户认证信息(包括权限信息、身份信息、详细信息)的 Authentication 实例。然后再将 Authentication实例设置到 SecurityContextHolder容器中。
UserDetailsService
负责加载用户信息,通过是通过数据库加载实现,也可以通过内存映射InMemoryDaoImpl实现。
UserDetails
用户详细信息,该接口提供了如下方法:
集成Spring Security
数据库设计
导入jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
业务实现类
public interface UserService extends UserDetailsService
{
Map<String, Collection<ConfigAttribute>>loadResourceDefine();
}
@Service
@Transactional(rollbackFor=Exception.class)
public class UserServiceImpl implements UserService
{
@Autowired
private UserMapper userMapper;
public UserDetails loadUserByUsername(String userId)
throws UsernameNotFoundException
{
//通过userid查询用户信息
User user= userMapper.getUserByUserId(userId);
if(user==null)
{
throw new RuntimeException("用户名或密码不正确");
}
//获取角色信息
List<Role> roles= userMapper.getRolesByUserOid(user.getOid());
return new org.springframework.security.core.userdetails.User(user.getUserId(), user.getPwd(), roles);
}
}
说明:业务实现类需要实现UserDetailsService接口,重写loadUserByUsername方法。
定义实体类
@TableName("fw_security_user")
public class User implements Serializable
{
private static final long serialVersionUID = -2167962784070236658L;
//设置主键名称
@TableId(value="oid",type=IdType.AUTO)
private Integer oid;
@TableField(value="userId")
private String userId;
@TableField(value="pwd")
private String pwd;
@TableField(value="userName")
private String userName;
@TableField(value="active")
private String active;
@TableField(value="gender")
private String gender;
@TableField(value="email")
private String email;
@TableField(value="mobile")
private String mobile;
}
@TableName(value = "fw_security_role")
public class Role implements GrantedAuthority ,Serializable
{
private static final long serialVersionUID = 1455102065732503507L;
@TableId(value="oid",type=IdType.AUTO)
private Integer oid;
@TableField(value="roleId")
private String roleId;
@TableField(value="active")
private String active;
@TableField(value="remark")
private String remark;
//返回权限信息
public String getAuthority()
{
return "ROLE_"+roleId;
}
}
Dao层实现
public interface UserMapper extends BaseMapper<User>
{
User getUserByUserId(String userId);
}
xml定义:
<select id="getUserByUserId" resultType="com.skywares.fw.security.pojo.User">
select
oid,
userId,
userName,
active,
gender,
mobile,
email,
pwd
from fw_security_user u
where u.userId=#{userId}
</select>
认证配置
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
@Autowired
private UserServiceImpl userService;
/**
* 静态资源不访问
*/
public void configure(WebSecurity web) throws Exception {
//配置静态文件不需要认证
web.ignoring().antMatchers("/js/**");
web.ignoring().antMatchers("/html/**");
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception
{
//验证用户
auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
}
//密码加密处理
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* DSL编程方式
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login","/loing-error","/css/**","/js/**").permitAll()
//任何请求需要认证
.anyRequest().authenticated()
// 登录页面
.and().formLogin().loginPage("/login").loginProcessingUrl("/login")
.failureUrl("/login-error").defaultSuccessUrl("/index")
//登出
.and().logout().logoutUrl("/logout")
//未授权页面访问
.exceptionHandling().accessDeniedPage( "/403" );
}
}
说明:loginProcessingUrl:自定义登录请求地址 defaultSuccessUrl:登录成功跳转地址 failureUrl :登录失败跳转地址
Controller代码
@Controller
public class LoginController
{
//登录请求
@RequestMapping("/login")
public String login() {
return "login";
}
//登录成功
@RequestMapping("/index")
public String index() {
return "index";
}
//登录错误,跳转到登录
@RequestMapping("/login-error")
public String loginError(Model model) {
// 登录错误
model.addAttribute("error", true);
return "login";
}
}
html页面
登录页面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<p th:if="${error}" class="error">用户名或密码错误</p>
<form th:action="@{/login}" method="post">
<label for="username">用户名</label>:
<input type="text" id="username" name="username" autofocus="autofocus"/> <br/>
<label for="password">用户密码</label>:
<input type="password" id="password" name="password"/> <br/>
<input type="submit" value="登录"/>
</form>
</body>
</html>
登录成功页面
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title>admin page</title>
</head>
<body>
success admin page!!!
</body>
</html>
测试
用户未登录请求,则跳转到登录页面
用户登录,用户名或者密码错误
说明:用户名或者密码错误会重定向到登录页面,且提示错误信息
用户登录,用户名和密码输出正确,则跳转到主页面
总结
本文讲解了Spring Boot 集成Spring Security进行登录认证,采用的是用户名和密码的方式进行认证,下一章讲解权限和角色的授权,如有问题,请随时反馈。