1.简单验证
首先springboot的版本是1.5.3,首先来一个简单的验证,只将认证的信息放到内存之中。
pom文件:
<?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 http://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>1.5.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.wx</groupId>
<artifactId>test-security15</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>test-security15</name>
<description>Demo project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</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>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
yml文件:
server:
port: 8080
#logging:
# level:
# root: WARN
# org.springframework.web: INFO
# org.springframework.security: INFO
spring:
thymeleaf:
mode: HTML5
encoding: UTF-8
cache: false
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/springcloud?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8
username: root
password: 133309
jpa:
hibernate:
ddl-auto: update
show-sql: true
controller:
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.wx.testsecurity15.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
/**
*
*/
@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);
return "login";
}
@GetMapping("/401")
public String accesssDenied() {
return "401";
}
}
配置,WebSecurityConfigurerAdapter
package com.wx.testsecurity15.config;
@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// @formatter:off
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/css/**", "/index").permitAll()
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/blogs/**").hasRole("USER")
.and()
.formLogin().loginPage("/login").failureUrl("/login-error")
.and()
.exceptionHandling().accessDeniedPage("/401");
http.logout().logoutSuccessUrl("/");
}
// @formatter:on
// @formatter:off
/* @Autowired
UserDetailsService userDetailsService;*/
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//通过AuthenticationManagerBuilder在内存中创建了一个认证用户信息,
//用户名为IronMan,密码为123456,有USER的角色
auth.inMemoryAuthentication()
.withUser("wang").password("123456").roles("USER");
}
}
前端页面:
欧克,启动工程,访问:
进入登陆页面:
登陆:
2.进行多个角色的验证
因为/user/index界面只有USER角色才能访问,所以新建一个admin用户,该用户只有ADMIN的角色,所以他不能访问/user/index
package com.wx.testsecurity15.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// @formatter:off
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/css/**", "/index").permitAll()
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/blogs/**").hasRole("USER")
.and()
.formLogin().loginPage("/login").failureUrl("/login-error")
.and()
.exceptionHandling().accessDeniedPage("/401");
http.logout().logoutSuccessUrl("/");
}
// @formatter:on
// @formatter:off
/* @Autowired
UserDetailsService userDetailsService;*/
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//通过AuthenticationManagerBuilder在内存中创建了一个认证用户信息,
//用户名为IronMan,密码为123456,有USER的角色
/*auth.inMemoryAuthentication()
.withUser("wang").password("123456").roles("USER");*/
//auth.inMemoryAuthentication().passwordEncoder(new MyPasswordEncoder()).withUser("wang").password("123456").roles("ADMIN");
auth.userDetailsService(userDetailsService());
//auth.userDetailsService(userDetailsService);
}
// @formatter:on
@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); // 在内存中存放用户信息
manager.createUser(User.withUsername("wang").password("123456").roles("USER").build());
manager.createUser(User.withUsername("admin").password("123456").roles("ADMIN").build());
return manager;
}
}
如果想要admin用户访问就给他添加USER的角色:
这时就能够访问了:
3.对方法级别的权限验证
比如具有管理员权限的用户才能够删除博客。
创建博客实体类:
package com.wx.testsecurity15.entity;
/**
* Created by fangzhipeng on 2017/5/15.
*/
public class Blog {
private Long id;
private String name;
private String content;
public Blog(Long id, String name, String content) {
this.id = id;
this.name = name;
this.content = content;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
service:
package com.wx.testsecurity15.service;
import com.wx.testsecurity15.entity.Blog;
import java.util.List;
/**
*
*/
public interface IBlogService {
List<Blog> getBlogs();
void deleteBlog(long id);
}
imp:
package com.wx.testsecurity15.service.impl;
import com.wx.testsecurity15.entity.Blog;
import com.wx.testsecurity15.service.IBlogService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Created by fangzhipeng on 2017/5/15.
*/
@Service
public class BlogService implements IBlogService {
private List<Blog> list=new ArrayList<>();
public BlogService(){
list.add(new Blog(1L, " spring in action", "good!"));
list.add(new Blog(2L,"spring boot in action", "nice!"));
}
@Override
public List<Blog> getBlogs() {
return list;
}
@Override
public void deleteBlog(long id) {
Iterator iter = list.iterator();
while(iter.hasNext()) {
Blog blog= (Blog) iter.next();
if (blog.getId()==id){
iter.remove();
}
}
}
}
controller层,添加方法级别的权限:
package com.wx.testsecurity15.controller;
import com.wx.testsecurity15.entity.Blog;
import com.wx.testsecurity15.service.impl.BlogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import java.util.List;
/**
* Created by fangzhipeng on 2017/5/15.
*/
@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);
}
@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);
}
}
普通用户登陆,删除博客:
admin用户登陆删除博客:
4.从数据库中读取用户的认证信息
需要添加数据库的依赖和JPA的依赖:
<!-- sql-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
yml文件:
server:
port: 8080
#logging:
# level:
# root: WARN
# org.springframework.web: INFO
# org.springframework.security: INFO
spring:
thymeleaf:
mode: HTML5
encoding: UTF-8
cache: false
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/springcloud?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8
username: root
password: 133309
jpa:
hibernate:
ddl-auto: update
show-sql: true
创建User实体:
package com.wx.testsecurity15.entity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
@Entity
public class User implements UserDetails, Serializable {
//UserDetails 是Spring Security认证信息的核心接口
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String username;
@Column
private String password;
@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;
/**authorities为权限点的集合*/
public User() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
/**
* getAuthorities()方法返回的是该用户设置的权限信息
* @return
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
public void setAuthorities(List<Role> authorities) {
this.authorities = authorities;
}
/**
* getUsername()方法为UserDetails的方法,这个方法不一定返回username,也可以是其他的用户信息
* 例如手机号码,邮箱地址,
* @return
*/
@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;
}
}
这个实体类实现了UserDeatils接口,并且重写了他的方法,ok,看一下这个UserDeatils接口:
因为关联到了角色类所以需要角色类,ok
package com.wx.testsecurity15.entity;
import org.springframework.security.core.GrantedAuthority;
import javax.persistence.*;
@Entity
public class Role implements GrantedAuthority {
//Role类实现接口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;
}
}
角色类实现了GrantedAuthority,接口,getAuthority权限点可以是任何的字符串,不一定是角色的字符串,本例权限点是从数据库中读取的Role表的nama字段
需要dao层去查询用户和其关联的角色:
package com.wx.testsecurity15.dao;
import com.wx.testsecurity15.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserDao extends JpaRepository<User, Long> {
User findByUsername(String username);
}
service层:
package com.wx.testsecurity15.service.impl;
import com.wx.testsecurity15.dao.UserDao;
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
private UserDao userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userRepository.findByUsername(username);
}
}
实现UserDetailsService接口,这个UserService就可以注入配置类中,ok配置类:
package com.wx.security15server.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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;
@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// @formatter:off
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/css/**", "/index").permitAll()
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/blogs/**").hasRole("USER")
.and()
.formLogin().loginPage("/login").failureUrl("/login-error")
.and()
.exceptionHandling().accessDeniedPage("/401");
http.logout().logoutSuccessUrl("/");
}
// @formatter:on
// @formatter:off
/* @Autowired
UserDetailsService userDetailsService;*/
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//通过AuthenticationManagerBuilder在内存中创建了一个认证用户信息,
//用户名为IronMan,密码为123456,有USER的角色
auth.inMemoryAuthentication()
.withUser("IronMan").password("123456").roles("USER");
//auth.userDetailsService(userDetailsService());
//auth.userDetailsService(userDetailsService);
}
// @formatter:on
// @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;
// }
}