Spring Security官网:https://spring.io/projects/spring-security#learn.
一、Spring Security简述
Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架。它是保护基于 Spring 的应用程序的事实上的标准。
Spring Security 是一个专注于为 Java 应用程序提供身份验证和授权的框架。与所有 Spring 项目一样,Spring Security 的真正强大之处在于它可以轻松扩展以满足自定义要求。
二、使用Spring Security
准备一个数据库
准备application.properties
#关闭缓存
spring.thymeleaf.cache=false
server.port=8081
#数据库配置
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/security?characterEncoding=UTF-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
准备pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.cao</groupId>
<artifactId>springsecurity</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springsecurity</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--引入mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!--引入mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--spring-boot-starter-security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--thymeleaf整合spring security-->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
</project>
准备实体类
package com.jsxl.pojo;
import lombok.Data;
@Data
public class User {
private Integer id;
private String name;
private String password;
private String auth;
}
准备UserMapper
package com.jsxl.mapper;
import com.jsxl.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface UserMapper {
//登录
User login(@Param("name") String ugh, @Param("password") String password);
//根据名字获取权限
User findUserByAuth(@Param("name") String name);
}
准备UserMapper.xml
<?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.jsxl.mapper.UserMapper">
<select id="login" resultType="com.jsxl.pojo.User">
select *
from security.user
where name = #{name}
and password = #{password};
</select>
<select id="findUserByAuth" resultType="com.jsxl.pojo.User">
select *
from security.user
where name = #{name}
</select>
</mapper>
UserService
package com.jsxl.service;
import com.jsxl.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
@Service("userDetailsService")//这里需要指定
public class UserService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
com.jsxl.pojo.User user = userMapper.findUserByAuth(name);
if (user == null) {
//数据库中没有,认证失败
throw new UsernameNotFoundException("没有找到该用户");
}
//权限拆分,字符串拆分成数组
String[] arr = user.getAuth().split(",");
//这里需要对密码进行加密,也可以使用java自带的MD5加密,通过工具类AuthorityUtils对数组进行转换
return new User(user.getName(), new BCryptPasswordEncoder().encode(user.getPassword()), AuthorityUtils.createAuthorityList(arr));
}
}
这里需要继承UserDetailsService实现loadUserByUsername方法
public interface UserDetailsService {
/** *根据用户名定位用户。在实际实现中,搜索*可能区分大小写,也可能不区分大小写,这取决于*实现实例的配置方式。在这种情况下,返回的<code>UserDetails</code> *对象的用户名可能与*实际请求的用户名不同。* * @param username用户名。* * @return一个完全填充的用户记录(never <code>null</code>) * *
@抛出UsernameNotFoundException如果用户不能被找到或用户没有*被授予的权限*/
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
AuthorityUtils工具类作用,可在源码中查看
* Creates a array of GrantedAuthority objects from a comma-separated string
* representation (e.g. "ROLE_A, ROLE_B, ROLE_C").
* 创建一个被授予权限的数组,由逗号分隔的字符串表示
* (例如:“ROLE_A,ROLE_B,ROLE_C”)。
Controller
package com.jsxl.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class RouterController {
@RequestMapping({"/", "/index"})
public String index() {
return "index";
}
@RequestMapping("/toLogin")
public String toLogin() {
return "views/login";
}
@RequestMapping("/level1/{id}")
public String level1(@PathVariable("id") int id) {
return "views/level1/" + id;
}
@RequestMapping("/level2/{id}")
public String level2(@PathVariable("id") int id) {
return "views/level2/" + id;
}
@RequestMapping("/level3/{id}")
public String level3(@PathVariable("id") int id) {
return "views/level3/" + id;
}
}
SecurityConfig
package com.jsxl.config;
import org.springframework.beans.factory.annotation.Autowired;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
//首页所有人可以访问,功能页有相应权限才能访问
//链式编程
//请求授权的规则
http.authorizeRequests()
.antMatchers("/","/toLogin").permitAll()
.antMatchers("/level1/**").hasAuthority("vip1")
.antMatchers("/level2/**").hasAuthority("vip2")
.antMatchers("/level3/**").hasAuthority("vip3");
//没有权限,默认到登录页面
http.formLogin()//自定义编写登录页面
.loginPage("/login")//登录页面设置
.loginProcessingUrl("/login")//登录访问路径
.defaultSuccessUrl("/index")//登陆成功后的路径
.usernameParameter("name")
.passwordParameter("password");
//防止网站攻击
http.csrf().disable();//登出可能存在失败的原因
//注销功能
http.logout().logoutSuccessUrl("/toLogin");
//开启记住我功能
http.rememberMe().rememberMeParameter("remember");
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
}
configure2个方法源码
*重载这个方法来配置{@link HttpSecurity}。
通常子类*不应该通过调用super来调用这个方法,因为它可能会覆盖它们的*配置。
默认的配置是:http.authorizeRequests().anyRequest().authenticated(). ().formLogin(). ().httpBasic();
// @formatter:off
protected void configure(HttpSecurity http) throws Exception {
logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().and()
.httpBasic();
}
/**
使用默认实现{@link #authenticationManager()}尝试*获得一个{@link #authenticationManager}。
如果被重写,* {@link AuthenticationManagerBuilder}应该被用来指定* {@link AuthenticationManager}。
{@link #authenticationManagerBean()}方法可以用来将结果* {@link AuthenticationManager}公开为一个Bean。
{@link #userDetailsServiceBean()}可以*被用来公开最后一个使用{@link AuthenticationManagerBuilder}创建的{@link UserDetailsService}作为Bean。
{@link UserDetailsService}也会自动在* {@link HttpSecurity#getSharedObject(Class)}上填充,以便与其他* {@link SecurityContextConfigurer}(即RememberMeConfigurer)一起使用。
例如,可以使用以下配置在内存中注册
在内存中公开一个UserDetailsService的身份验证:
configure(AuthenticationManagerBuilder auth) {
身份验证使用名为。的用户在内存中启用基于身份验证
“user" 和“;admin"
.inMemoryAuthentication () .withUser(“user") .password(“password") .roles(“user"), ()
.withUser(“admin") .password(“password") .roles(“USER",“admin");
}
将UserDetailsService公开为一个Bean
*/
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
this.disableLocalConfigureAuthenticationBldr = true;
}
spring boot启动类
package com.jsxl;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.jsxl.mapper")
public class SpringsecurityApplication {
public static void main(String[] args) {
SpringApplication.run(SpringsecurityApplication.class, args);
}
}
测试结果