一、什么是微服務

二、權限管理數據模型

*添加角色
*為角色分配菜單
*添加用戶
*為用戶分配菜單
一共5張表:
security.sql(微服務登錄中有)
拿這修改就行
微服務權限管理案例主要功能:
使用技術說明
1、登錄(認證)
2、添加角色
3、為角色分配菜單
4、添加用戶
5、為用戶分配角色
1、Maven
創建父工程:管理項目依賴版本
創建子模塊:使用具體依賴
2.SpringBoot 本質就是Spring
3.MyBatisPlus 操作數據庫框架
4.SpringCloud
(1)GateWay 網關
(2)注冊中心 Nacos
其他技術:
Redis Jwt Swagger
搭建項目工程
1、創建父工程 acl_parent :管理依賴版本
2、在父工程創建子模塊
(1)common
*service_base:工具類
*spring_security :權限配置
(2)infrastructure
*api_gateway: 網關
(3)service
*service_acl:權限管理微服務模塊
接下來:引入依賴(微服務登錄中有)
引入完依賴啟動redis和Nacos
Nacos介紹
上面是Windows下面是Linux
訪問地址:http://localhost:8848/nacos/
默認用戶名密碼:nacos
工具類
編寫common工具類
1、編寫common里面需要的工具類
2.編寫security工具類
一.先寫密碼處理工具類
@Component
public class DefaultPasswordEncoder implements PasswordEncoder {
public DefaultPasswordEncoder(){
this(-1);
}
public DefaultPasswordEncoder(int strength){
}
//進行MD5加密
@Override
public String encode(CharSequence charSequence) {
return MD5.encrypt(charSequence.toString());
}
//進行密碼對比
@Override
public boolean matches(CharSequence charSequence, String encodedPassword) {
return encodedPassword.equals(MD5.encrypt(charSequence.toString()));
}
}
二.token操作工具類
使用jwt生成token
//1.使用jwt根據用戶名生成token
@Component
public class TokenManager {
//token有效時長
private long tokenEcpriation=24*60*60*1000;
//編碼秘鑰
private String tokenSignKey ="123456";
//1.使用jwt根據用戶名生成token
public String createToken(String username){
String token = Jwts.builder().setSubject(username)
.setExpiration(new Date(System.currentTimeMillis()+tokenEcpriation))
.signWith(SignatureAlgorithm.HS512 ,tokenSignKey).compressWith(CompressionCodecs.GZIP).compact();
return token;
}
//2.根據token字符串得到用戶信息
public String getUserInfoFromToken(String token){
String userinfo = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token).getBody().getSubject();
return userinfo;
}
//3刪除token
public void removeToken(String token){
}
}
三.退出處理器
//退出處理器
public class TokenLogoutHandler implements LogoutHandler {
private TokenManager tokenManager;
private RedisTemplate redisTemplate;
public TokenLogoutHandler(TokenManager tokenManager,RedisTemplate redisTemplate){
this.tokenManager =tokenManager;
this.redisTemplate=redisTemplate;
}
@Override
public void logout(HttpServletRequest request,HttpServletResponse httpServletResponse, Authentication authentication) {
//1.從header里面獲取token
//2.token不為空,移除token,從redis刪除token
String token = request.getHeader("token");
if (token!=null){
//移除
tokenManager.removeToken(token);
//從token獲取用戶名
String username = tokenManager.getUserInfoFromToken(token);
redisTemplate.delete(username);
}
}
}
未授權統一處理類
public class UnauthEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
ResponseUtil.out(httpServletResponse, R.error());
}
}
編寫security認證過濾器
准備兩個實體類
User
@Data
@ApiModel(description = "用戶實體類")
public class User implements Serializable {
private String username;
private String password;
private String nickName;
private String salt;
private String token;
}
SecurityUser類
@Data
@Slf4j
public class SecurityUser implements UserDetails {
//當前登錄用戶
private transient User currentUserInfo;
//當前權限
private List<String> permissionValueList;
public SecurityUser() {
}
public SecurityUser(User user) {
if (user != null) {
this.currentUserInfo = user;
}
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<>();
for(String permissionValue : permissionValueList) {
if(StringUtils.isEmpty(permissionValue)) continue;
SimpleGrantedAuthority authority = new
SimpleGrantedAuthority(permissionValue);
authorities.add(authority);
}
return authorities;
}
@Override
public String getPassword() {
return currentUserInfo.getPassword();
}
@Override
public String getUsername() {
return currentUserInfo.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
在編寫security認證過濾
1.認證過濾器:
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {
private TokenManager tokenManager;
private RedisTemplate redisTemplate;
private AuthenticationManager authenticationManager;
public TokenLoginFilter(AuthenticationManager authenticationManager,TokenManager tokenManager,RedisTemplate redisTemplate){
this.authenticationManager=authenticationManager;
this.tokenManager=tokenManager;
this.redisTemplate=redisTemplate;
this.setPostOnly(false);
this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/acl/login","POST"));
}
//1.獲取表單提交用戶名和密碼
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException{
//獲取表單提交數據
try {
User user = new ObjectMapper().readValue(request.getInputStream(), User.class);
return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword(),
new ArrayList<>()));
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException();
}
}
//2.認證成功的方法
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {
//認證成功,得到認證成功之后的信息
SecurityUser user = (SecurityUser) authResult.getPrincipal();
//根據用戶名生成token
String token = tokenManager.createToken(user.getCurrentUserInfo().getUsername());
//把用戶名稱和用戶權限列表放到redis
redisTemplate.opsForValue().set(user.getCurrentUserInfo().getUsername(),user.getPermissionValueList());
//返回token
ResponseUtil.out(response, R.ok().data("token",token));
}
//3.認證失敗調用的方法
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed)
throws IOException, ServletException {
ResponseUtil.out(response, R.error());
}
}
2.授權過濾器
public class TokenAuthFilter extends BasicAuthenticationFilter {
private TokenManager tokenManager;
private RedisTemplate redisTemplate;
public TokenAuthFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
this.tokenManager= tokenManager;
this.redisTemplate=redisTemplate;
}
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
//獲取當前認證成功用戶權限信息
UsernamePasswordAuthenticationToken authRequest= getAuthentication(request);
//判斷如果有權限信息,放到權限上下文中
if (authRequest!=null){
SecurityContextHolder.getContext().setAuthentication(authRequest);
}
chain.doFilter(request,response);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request){
//1.從header獲取token
String token = request.getHeader("token");
if (token!=null){
//從token獲取用戶名
String username = tokenManager.getUserInfoFromToken(token);
//從redis獲取對應權限列表
List<String>permissionValueList = (List<String>) redisTemplate.opsForValue().get(username);
//創建一個Collection<GrantedAuthority>集合
Collection<GrantedAuthority> authority=new ArrayList<>();
//遍歷
for (String permissionValue : permissionValueList) {
SimpleGrantedAuthority auth=new SimpleGrantedAuthority(permissionValue);
authority.add(auth);
}
return new UsernamePasswordAuthenticationToken(username,token,authority);
}
return null;
}
}
編寫核心配置類
@Configuration
public class TokenWebSecurityConfig extends WebSecurityConfigurerAdapter {
private TokenManager tokenManager;
private RedisTemplate redisTemplate;
private DefaultPasswordEncoder defaultPasswordEncoder;
private UserDetailsService userDetailsService;
@Autowired
public TokenWebSecurityConfig(UserDetailsService userDetailsService,DefaultPasswordEncoder defaultPasswordEncoder,
TokenManager tokenManager,RedisTemplate redisTemplate ){
this.userDetailsService=userDetailsService;
this.defaultPasswordEncoder=defaultPasswordEncoder;
this.tokenManager=tokenManager;
this.redisTemplate=redisTemplate;
}
/**
* 配置設置
*/
//設置退出的地址和 token,redis 操作地址
@Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling()
.authenticationEntryPoint(new UnauthEntryPoint())//沒有權限訪問
.and().csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and().logout().logoutUrl("/admin/acl/index/logout")//退出路徑
.addLogoutHandler(new TokenLogoutHandler
(tokenManager,redisTemplate)).and()
.addFilter(new TokenLoginFilter
(authenticationManager(),tokenManager, redisTemplate))
.addFilter(new TokenAuthFilter(authenticationManager(), tokenManager, redisTemplate)).httpBasic();
}
/**
* 調用userDetailsService和密碼處理
* */
@Override
public void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(defaultPasswordEncoder);
}
/**
* * 配置哪些請求不攔截
* 不進行認證,可以直接訪問
* */
@Override
public void configure(WebSecurity web)
throws Exception {
web.ignoring().antMatchers("/api/**", "/swagger-ui.html/**");
}
}
編寫UserDetailsService
如圖:

代碼做參考

整合權限管理模塊
application.properties
#服務端口
server.port=8009
#服務器
spring.application.name=service-acl
#mysql數據庫連接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3307/acldb?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root
#redis連接
spring.redis.host=192.168.24.144
spring.redis.port=6379
spring.redis.database= 0
spring.redis.timeout=1800000
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1
#最大阻塞等待時間(負數表示沒有限制)
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0
#最小空閑
#配置mapper xml文件的路徑
mybatis-plus.mapper-locations=classpath:com/atguigu/aclservice/mapper/xml/*.xml
#nacos服務地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
#返回json的全局時間格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

前端頁面:
前端啟動命令: npm run dev
前端啟動報如下錯誤:
使用npm run dev
運行,出現如下錯誤 :
Failed to compile.
./src/styles/index.scss (./node_modules/css-loader??ref--11-1!./node_modules/postcss-loader/lib??ref--11-2!./node_modules/sass-loader/lib/loader.js??ref--11-3!./src/styles/index.scss)
Module build failed (from ./node_modules/sass-loader/lib/loader.js):
Error: Missing binding E:\test\node_modules\node-sass\vendor\win32-x64-67\binding.node
Node Sass could not find a binding for your current environment: Windows 64-bit with Node.js 11.x
Found bindings for the following environments:
- Windows 64-bit with Node.js 10.x
This usually happens because your environment has changed since running `npm install`.
Run `npm rebuild node-sass` to download the binding for your current environment.
首先在項目目錄下依次安裝以下文件:
npm install node-sass
npm i node-sass -D
如果繼續報錯,會提醒安裝element-ui就可以運行項目了,我們繼續在項目目錄下安裝它就好了:
npm install --save element-ui
然后運行項目就OK啦,終於不報錯啦!
后端錯誤:java.lang.NullPointerException: null
解決方法:
重新啟動就可以登錄了