spring-boot-starter-security是对Spring Security的一个封装,具体如下
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
进入里面的 文件:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starters</artifactId> //父类是spring-boot-starters
<version>1.5.3.RELEASE</version>
</parent>
<artifactId>spring-boot-starter-security</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId> //加入的起步依赖,spring-boot-starter
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId> //加入了 aop
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId> //封装了security-config,移除aop
<exclusions>
<exclusion>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId> //封装了security-web,移除aop
<exclusions>
<exclusion>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId> // 开始 security
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId> //开始 thymeleaf 依赖
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> // 开始 web
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId> //模板 和 security整合
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> //开始 测试
<scope>test</scope>
</dependency>
<!-- sql-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId> //开始 JPA
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> //mysql 驱动
</dependency>
</dependencies>
配置Spring Security 的AuthenticationManagerBuilder
@EnableWebSecurity //1 开启 webSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter { //2 继承配置类 WebSecurityConfigurerAdapter
@Autowired //3 注入 AuthenticationManagerBuilder
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("forezp").password("123456").roles("USER") ;
//4 具体的配置, 内存级别的,用户名 密码 角色
}
集成了 getRemoteUser() getUserPrincipal() login(string,string) logout() API
HttpSecurity 哪些需要认证,哪些不需要认证
@EnableWebSecurity
@Configuration //指出这个类是配置类
@EnableGlobalMethodSecurity(prePostEnabled = true) //开户方法验证
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests() //认证的请求,开始匹配
.antMatchers("/css/**", "/index").permitAll() //所有css 请求和 index 放行
.antMatchers("/user/**").hasRole("USER") //所有user请求,和 blogs 请求,需要 user权限
.antMatchers("/blogs/**").hasRole("USER")
.and()
.formLogin().loginPage("/login").failureUrl("/login-error") //登录的请求是login,失败的请求是 login-error
.and()
.exceptionHandling().accessDeniedPage("/401"); //异常拒绝的请求是 /401
http.logout().logoutSuccessUrl("/"); //登录成功的请求是 /
}
配置application.yml
spring:
thymeleaf:
mode: HTML5
encoding: UTF-8
cache: false
action
@Controller
public class MainController {
@RequestMapping("/")
public String root() {
return "redirect:/index"; //重定向
}
@RequestMapping("/index")
public String index() {
return "index";
}
@RequestMapping("/user/index")
public String userIndex() {
return "user/index";
}
@RequestMapping("/login")
public String login() {
return "login";
}
@RequestMapping("/login-error")
public String loginError(Model model) {
model.addAttribute("loginError", true); //在Model 放 数据
return "login";
}
@GetMapping("/401")
public String accesssDenied() {
return "401";
}
}
login.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Login page</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" /> //用了模板,直接 th:href="@{}"
</head>
<body>
<p th:if="${loginError}" class="error">用户名或密码错误</p> //取出 放置的错误 信息 如果存在的话
<form th:action="@{/login}" method="post"> //th:action="@{}" 提交的地址
<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>
<p><a href="/index" th:href="@{/index}">返回首页</a></p> //返回到首页
</body>
</html>
index.html
<div th:fragment="logout" sec:authorize="isAuthenticated()">
登录用户: <span sec:authentication="name"></span> | //登录用户
用户角色: <span sec:authentication="principal.authorities"></span> //角色
<div>
<form action="#" th:action="@{/logout}" method="post"> //退出的请求是 post
<input type="submit" value="登出" />
</form>
</div>
</div>
<ul>
<li>点击<a href="/user/index" th:href="@{/user/index}">去/user/index保护的界面</a></li>
</ul>
user/index.html
<div th:substituteby="index::logout"></div> //使用 index页面中 logout 片段
<h1>这个界面是被保护的界面</h1>
<p><a href="/index" th:href="@{/index}">返回首页</a></p>
<p><a href="/blogs" th:href="@{/blogs}">管理博客</a></p>
401.html
<div sec:authorize="isAuthenticated()"> //这里和上面的一样,使用片段 也挺好的
<p>已有用户登录</p>
<p>用户: <span sec:authentication="name"></span></p>
<p>角色: <span sec:authentication="principal.authorities"></span></p>
</div>
<div sec:authorize="isAnonymous()"> //未登录 直接访问401.html
<p>未有用户登录</p>
</div>
<p>
拒绝访问!
</p>
使用 th sec 标签要引入 >xmlns:sec=“http://www.thymeleaf.org/thymeleaf-extras-springsecurity4”>
第二版,配置类中 使用简单的userDetailsService
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
}
@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); // 在内存中存放用户信息
manager.createUser(User.withUsername("forezp").password("123456").roles("USER").build());
manager.createUser(User.withUsername("admin").password("123456").roles("USER","ADMIN").build());
return manager;
}
开启方法级别的保护
@EnableGlobalMethodSecurity(prePostEnabled = true)
意思是 @PreAuthorize 和 @PostAuthorize 是否可用 @PreAuthorize(“hasRole(‘ADMIN’,’******’)”) @PreAuthorize(“hasAuthorize(‘ROLE_ADMIN’,’******’)”)
测试方法级别的保护
public class Blog {
private Long id;
private String name;
private String content;
public interface IBlogService {
List<Blog> getBlogs();
void deleteBlog(long id);
}
@Service
public class BlogService implements IBlogService {
private List<Blog> list=new ArrayList<>(); //List 存储
public BlogService(){
list.add(new Blog(1L, " spring", "good!")); //加入数据
list.add(new Blog(2L,"spring boot", "nice!"));
}
@Override
public List<Blog> getBlogs() { //获得数据
return list;
}
@Override
public void deleteBlog(long id) { //删除
Iterator iter = list.iterator(); //先取得Iterator
while(iter.hasNext()) { // 如果有下一个,取出,并且和id比较
Blog blog= (Blog) iter.next();
if (blog.getId()==id){
iter.remove();
}
}
}
}
@RestController
@RequestMapping("/blogs")
public class BlogController {
@Autowired
BlogService blogService;
@GetMapping
public ModelAndView list(Model model) {
List<Blog> list =blogService.getBlogs();
model.addAttribute("blogsList", list);
return new ModelAndView("blogs/list", "blogModel", model); //跳转页面,为model起名字,放入的model
}
@PreAuthorize("hasAuthority('ROLE_ADMIN')") //在之前就校验 是否 有权限
@GetMapping(value = "/{id}/deletion")
public ModelAndView delete(@PathVariable("id") Long id, Model model) {
blogService.deleteBlog(id);
model.addAttribute("blogsList", blogService.getBlogs());
return new ModelAndView("blogs/list", "blogModel", model);
}
}
<table >
<thead>
<tr>
<td>博客编号</td>
<td>博客名称</td>
<td>博客描述</td>
</tr>
</thead>
<tbody>
<tr th:each="blog: ${blogModel.blogsList}"> //取出model,中 的 list 放在 blog 中
<td th:text="${blog.id}"></td>
<td th:text="${blog.name}"></td>
<td th:text="${blog.content}"></td>
<td>
<div >
<a th:href="@{'/blogs/' + ${blog.id}+'/deletion'}">
删除
</a>
</div>
</td>
</tr>
</tbody>
</table>
JPA
1,引入依赖(jpa 数据库驱动)
2,配置数据库连接属性 和 数据库ddl 生成方式
3,写 Entity类
security:
user:
name: lisi
password: 123456
role: USER
basic:
enabled: true //设置为false就关闭了,在没有配置 WebSecurityConfigurerAdapter的情况下
update 如果启动时表格式不一致则更新表,原有数据保留
create 启动时删数据库中的表,然后创建,退出时不删除数据表
create-drop 启动时删数据库中的表,然后创建,退出时删除数据表 如果表不存在报错
validate 项目启动表结构进行校验 如果不一致则报错
删除意思:sessionFactory一关闭,表就自动删除(hibernate
User
@Entity
public class User implements UserDetails, Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String username;
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
private List<Role> authorities;
@Column
private String password;
public User() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
public void setAuthorities(List<Role> authorities) {
this.authorities = authorities;
}
@Override
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
role
@Entity
public class Role implements GrantedAuthority {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Override
public String getAuthority() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
service dao
public interface UserDao extends JpaRepository<User, Long>{
User findByUsername(String username);
}
@Service
public class UserService implements UserDetailsService {
@Autowired
private UserDao userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userRepository.findByUsername(username);
}
}