工程目录结构
主类:
package com.zhisen.uud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@SpringBootApplication
public class UudApplication {
/*
* 认证使用 uaa/oauth/token
*
*/
public static void main(String[] args) {
SpringApplication.run(UudApplication.class, args);
}
}
WebSecurity配置类
package com.zhisen.uud.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
private UserDetailsService userDetailsService;
// 注入登录成功返回处理对象
@Autowired
private AuthenticationSuccessHandler successHandler;
// 注入登录失败的处理对象
@Autowired
private AuthenticationFailureHandler failureHandler;
// 登出成功处理的对象
@Autowired
private LogoutSuccessHandler logoutSuccessHandler;
// 未登录系统
@Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
// 无权限访问
@Autowired
private AccessDeniedHandler accessDeniedHandler;
// 认证管理器,security,负责管理认证,能不能登录。
@Bean
public AuthenticationManager authenticationManager() throws Exception{
return super.authenticationManager();
}
// 初始化认证管理器
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// TODO Auto-generated method stub
// 指定使用自己定的类来加载
auth.userDetailsService(userDetailsService);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// 解决跨域问题
http.csrf().disable();
http.formLogin().permitAll()
// 声明一个post调用成功之后的接口
.successHandler(successHandler)
// 实现一个post调用失败之后的接口
.failureHandler(failureHandler)
.and()
.authorizeRequests()
// 注释掉的这句话意味着只要访问 / 下面的接口全部都可以访问
// .antMatchers("/*").permitAll()
// 如果想要访问/account下的接口需要具备权限orede_search
.antMatchers("/account/*").hasAuthority("orede_search")
// 如果想要访问/test下的接口必须是a角色
// .antMatchers("/test/*").hasRole("a")
// 必须同时具备是角色a并且拥有权限orede_search
// .antMatchers("/test/*").access("hasRole('a') and hasAuthority('orede_search')")
// 是角色a或b的一种就可以
// .antMatchers("/test/*").hasAnyRole("a","b")
// 拥有orede_search或者niubi一种权限即可
.antMatchers("/test/*").hasAnyAuthority("orede_search","niubi")
// 其他的接口都是登录可访问
.anyRequest().authenticated()
.and()
.logout().permitAll()
// 登出成功之后调用的回调函数
.logoutSuccessHandler(logoutSuccessHandler)
// 清除缓存
.deleteCookies("JSESSIONID")
.and()
.exceptionHandling()
// 设置未登录系统报异常调用的函数
.authenticationEntryPoint(authenticationEntryPoint)
// 设置无权限报异常调用的函数
.accessDeniedHandler(accessDeniedHandler);
}
}
accessDeniedHandler
package com.zhisen.uud.config.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
@Component
public class accessDeniedHandler implements AccessDeniedHandler{
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e)
throws IOException, ServletException {
// TODO Auto-generated method stub
response.setContentType("test/json;charset=utf-8");
response.getWriter().write("{\"msg\":\"无权限访问资源!\"}");
}
}
authentication
package com.zhisen.uud.config.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
@Component
public class authentication implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException {
// TODO Auto-generated method stub
response.setContentType("test/json;charset=utf-8");
response.getWriter().write("{\"msg\":\"未登录系统访问接口异常!\"}");
}
}
failureHandler
package com.zhisen.uud.config.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
@Component
public class failureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException e) throws IOException, ServletException {
// TODO Auto-generated method stub
/*
* 失败之后调用的内容
*/
// response.setContentType("test/json;charset=utf-8");
// response.getWriter().write("{\"msg\":\"登陆失败\"}");
// 返回json数据
String result = null;
if (e instanceof AccountExpiredException) {
// 账号过期
result = "{\"msg\":\"账号过期\"}";
} else if (e instanceof InternalAuthenticationServiceException) {
// 账号密码不正确
result = "{\"msg\":\"账号密码不正确\"}";
} else if (e instanceof BadCredentialsException) {
// 密码错误
result = "{\"msg\":\"用户不存在或密码错误\"}";
} else if (e instanceof CredentialsExpiredException) {
// 密码过期
result = "{\"msg\":\"密码过期\"}";
} else if (e instanceof DisabledException) {
// 账号不可用
result = "{\"msg\":\"账号不可用\"}";
} else if (e instanceof LockedException) {
// 账号锁定
result = "{\"msg\":\"账号锁定\"}";
}else {
// 其他错误
result = "{\"msg\":\"其他错误\"}";
}
// 处理编码方式,防止中文乱码的情况
response.setContentType("text/json; charset=utf-8");
// 塞到HttpServletResponse中返回给前台
response.getWriter().write(result);
}
}
logoutSuccessHandler
package com.zhisen.uud.config.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
@Component
public class logoutSuccessHandler implements LogoutSuccessHandler{
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
// TODO Auto-generated method stub
response.setContentType("test/json;charset=utf-8");
response.getWriter().write("{\"msg\":\"登出成功\"}");
}
}
successHandler
package com.zhisen.uud.config.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
@Component
public class successHandler implements AuthenticationSuccessHandler{
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
/*
* authentication包含了userdetils里添加的内容
* 这里可以给前端返回权限等值
*/
// TODO Auto-generated method stub
response.setContentType("test/json;charset=utf-8");
response.getWriter().write("{\"msg\":\"登陆成功\"}");
}
}
AccountController
package com.zhisen.uud.controller;
import java.util.HashMap;
import java.util.Map;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("account")
public class AccountController {
// 无参数传递
@GetMapping("/test")
public Map<String,Object> test1(){
Map<String,Object> result = new HashMap<String, Object>();
result.put("A01", "post");
try {
// Thread.sleep(2000);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println("A01执行了!");
return result;
}
// 无参数传递
@GetMapping("/test1")
public Map<String,Object> test2(){
Map<String,Object> result = new HashMap<String, Object>();
result.put("A02", "post");
try {
// Thread.sleep(2000);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println("A02执行了!");
return result;
}
}
restTemplateController
package com.zhisen.uud.controller;
import java.util.HashMap;
import java.util.Map;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("account")
public class AccountController {
// 无参数传递
@GetMapping("/test")
public Map<String,Object> test1(){
Map<String,Object> result = new HashMap<String, Object>();
result.put("A01", "post");
try {
// Thread.sleep(2000);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println("A01执行了!");
return result;
}
// 无参数传递
@GetMapping("/test1")
public Map<String,Object> test2(){
Map<String,Object> result = new HashMap<String, Object>();
result.put("A02", "post");
try {
// Thread.sleep(2000);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println("A02执行了!");
return result;
}
}
Account
package com.zhisen.uud.dao.entity;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class Account implements UserDetails{
private Integer id;
private String userName;
private String password;
Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
@Override
public Collection<GrantedAuthority> getAuthorities() {
// TODO Auto-generated method stub
return this.authorities;
}
@Override
public String getPassword() {
// TODO Auto-generated method stub
return this.password;
}
@Override
public String getUsername() {
// TODO Auto-generated method stub
return this.userName;
}
@Override
public boolean isAccountNonExpired() {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isAccountNonLocked() {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isCredentialsNonExpired() {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isEnabled() {
// TODO Auto-generated method stub
return true;
}
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;
}
}
UserDetailServiceImpl
package com.zhisen.uud.service.impl;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import com.zhisen.uud.dao.entity.Account;
@Service
//只负责提供信息,不负责验证
public class UserDetailServiceImpl implements UserDetailsService{
@Autowired
PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (!"admin".equals(username)) {
throw new UsernameNotFoundException(username);
}
//TODO:使用username参数到后台查询用户信息,使用username参数到数据库查询权限信息
Account account = new Account();
account.setId(0);
// admin 是数据库中正式存在的一个账号(登录名称)
account.setUserName("admin");
// 加密过程在后续连接数据库后应该取消
account.setPassword(this.passwordEncoder.encode("123456"));
// 该列表应该从数据库查询出来
List<String> permisons = new ArrayList<String>();
permisons.add("orede_search");
permisons.add("ROLE_a");
// 如果名称不存在,应该抛出异常 UsernameNotFoundException
for (String permisonsStr : permisons) {
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permisonsStr);
account.getAuthorities().add(grantedAuthority);
}
return account;
}
}
UserDetailServiceImpl
server:
port: 18889
spring:
application:
name: cslcp
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://172.16.122.112:3306/zzz?serverTimezone=GMT%2B8&allowMultiQueries=true&characterEncoding=UTF-8
username: root
password: root
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 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.5.2</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.zhisen</groupId>
<artifactId>security</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>security</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
数据库
create table oauth_client_details (
client_id VARCHAR(256) PRIMARY KEY,
resource_ids VARCHAR(256),
client_secret VARCHAR(256),
scope VARCHAR(256),
authorized_grant_types VARCHAR(256),
web_server_redirect_uri VARCHAR(256),
authorities VARCHAR(256),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additional_information VARCHAR(4096),
autoapprove VARCHAR(256)
);
INSERT INTO oauth_client_details
(client_id, client_secret, scope, authorized_grant_types,
web_server_redirect_uri, authorities, access_token_validity,
refresh_token_validity, additional_information, autoapprove)
VALUES
('user-client', '$2a$10$o2l5kA7z.Caekp72h5kU7uqdTDrlamLq.57M1F6ulJln9tRtOJufq', 'all',
'refresh_token,password', null, null, 3600, 36000, null, true);
INSERT INTO oauth_client_details
(client_id, client_secret, scope, authorized_grant_types,
web_server_redirect_uri, authorities, access_token_validity,
refresh_token_validity, additional_information, autoapprove)
VALUES
('order-client', '$2a$10$GoIOhjqFKVyrabUNcie8d.ADX.qZSxpYbO6YK4L2gsNzlCIxEUDlW', 'all',
'refresh_token,password', null, null, 3600, 36000, null, true);
create table oauth_access_token (
token_id VARCHAR(128),
token BLOB,
authentication_id VARCHAR(128) PRIMARY KEY,
user_name VARCHAR(128),
client_id VARCHAR(128),
authentication BLOB,
refresh_token VARCHAR(128)
);
create table oauth_client_token (
token_id VARCHAR(256),
token BLOB,
authentication_id VARCHAR(256) PRIMARY KEY,
user_name VARCHAR(256),
client_id VARCHAR(256)
);
create table oauth_refresh_token (
token_id VARCHAR(256),
token BLOB,
authentication BLOB
);
create table oauth_code (
code VARCHAR(256),
authentication BLOB
);
create table oauth_approvals (
userId VARCHAR(256),
clientId VARCHAR(256),
scope VARCHAR(256),
status VARCHAR(10),
expiresAt TIMESTAMP,
lastModifiedAt TIMESTAMP
);
create table ClientDetails (
appId VARCHAR(256) PRIMARY KEY,
resourceIds VARCHAR(256),
appSecret VARCHAR(256),
scope VARCHAR(256),
grantTypes VARCHAR(256),
redirectUrl VARCHAR(256),
authorities VARCHAR(256),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additionalInformation VARCHAR(4096),
autoApproveScopes VARCHAR(256)
);
制作整理不易,以上内容均为原创(参考了部分官方文档和老师整理的案例)。如要引用请附上本文链接,如有疑问可以在评论区畅所欲言,作者看到会第一时间回复,欢迎交流!