首页 IT程序员内容详情

pringBoot集成Security,前后端分离的SecurityConfig配置

2021-09-26 47576 编程艺术家

前后端分离下保持状态是个问题,但是我这里不涉及分布式,所以用不上JWT,JWT根据项目情况来决定是否使用.


Authentication对象会记录用户的状态,所以不用定义一个token,从Authentication中

getAuthorities方法获取用户的状态

如果使用JWT来保持状态的话,就在拦截器上对token进行解码判断就行

springboot-security maven依赖


<dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-security</artifactId>

</dependency>

先贴上Model代码:


package com.jlau.schoollocationsystem.model;

 

import org.springframework.data.annotation.Id;

import org.springframework.data.mongodb.core.mapping.Document;

import org.springframework.security.core.GrantedAuthority;

import org.springframework.security.core.authority.SimpleGrantedAuthority;

import org.springframework.security.core.userdetails.UserDetails;

 

import java.io.Serializable;

import java.util.ArrayList;

import java.util.Collection;

import java.util.List;

 

/**

 * Created by cxr1205628673 on 2020/3/16.

 */

 

@Document("user")

public class OrdinaryUser extends User implements UserDetails,Serializable{

    //User要继承 UserDetails接口,实现是否可用、上锁、getAuthorties等方法

    @Id

    private String id;

    private String username;

    private String password;

    private List<Role> roles;

 

    public List<Role> getRoles() {

        return roles;

    }

 

    public void setRoles(List<Role> roles) {

        this.roles = roles;

    }

 

    @Override

    public Collection<? extends GrantedAuthority> getAuthorities() {

        List<SimpleGrantedAuthority> auth = new ArrayList<>();

 

        for (Role role:roles) {

            auth.add(new SimpleGrantedAuthority(role.getName()));

        }

        return auth;

    }

 

    @Override

    public boolean isAccountNonExpired() {

        return true;

    }

 

    @Override

    public boolean isAccountNonLocked() {

        return true;

    }

 

    @Override

    public boolean isCredentialsNonExpired() {

        return true;

    }

 

    @Override

    public boolean isEnabled() {

        return true;

    }

 

    public String getUsername() {

        return username;

    }

 

    public void setUsername(String username) {

        this.username = username;

    }

 

    public String getPassword() {

        return password;

    }

 

    public void setPassword(String password) {

        this.password = password;

    }

}

@Entity

@Table(name = "role")

public class Role implements GrantedAuthority{

    //Role类需要实现GrantedAuthority接口,让security判断是否有权限

    @Id

    @Column

    @GeneratedValue

    private Integer id;

    @Column

    private String name;

 

    @JsonIgnore

    @ManyToMany(mappedBy = "roles")

    List<User> users;

 

    public Integer getId() {

        return id;

    }

 

    public void setId(Integer id) {

        this.id = id;

    }

 

    public String getName() {

        return name;

    }

 

    public void setName(String name) {

        this.name = name;

    }

 

    @Override

    public String getAuthority() {

        return name;

    }

}

下面是websecurityconfig代码继承adaptor... 


 


package com.jlau.schoollocationsystem.configuration;

 

import com.fasterxml.jackson.databind.ObjectMapper;

import com.jlau.schoollocationsystem.service.UserService;

import com.jlau.schoollocationsystem.utils.ResponseMsg;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.security.access.AccessDeniedException;

import org.springframework.security.config.Customizer;

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.WebSecurityConfigurerAdapter;

import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer;

import org.springframework.security.core.Authentication;

import org.springframework.security.core.AuthenticationException;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import org.springframework.security.crypto.password.PasswordEncoder;

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.LogoutHandler;

import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

 

import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import java.io.IOException;

import java.io.PrintWriter;

import java.util.HashMap;

import java.util.Map;

 

/**

 * Created by cxr1205628673 on 2020/3/16.

 */

@Configuration

public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

    @Autowired

    private UserService userService;

 

    @Bean

    public PasswordEncoder getPasswordEncoder(){

        //前后端分离要注入一个PasswordEncoder来给successHandler使用

        return new BCryptPasswordEncoder();

    }

    @Override

    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        super.configure(auth);

        auth.userDetailsService(userService);

    }

 

    @Override

    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()

                .antMatchers("/admin/**")

                .hasRole("ADMIN")

                .antMatchers("/user/**")

                .access("hasAnyRole('ADMIN','USER')")

                .antMatchers("/db/**")

                .hasRole("DBA")

                .antMatchers("/register")

                .permitAll()

                .anyRequest()//其他请求登录后可访问

                .authenticated()

                .and()

                .formLogin()

                //.loginPage("/login") 前后端分离,前端不能收到302

                .loginProcessingUrl("/user/login")

                .usernameParameter("username")

                .passwordParameter("password")

                .successHandler(new AuthenticationSuccessHandler() {

                    @Override

                    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {

                        httpServletResponse.setContentType("application/json;charset=utf-8");

                        ObjectMapper objectMapper = new ObjectMapper();

                        Map map = new HashMap();

                        map.put("authenties",authentication.getAuthorities());

                        map.put("name",authentication.getName());

                        ResponseMsg rsp = new ResponseMsg(200,"ok",map);

                        PrintWriter out = httpServletResponse.getWriter();

                        out.write(objectMapper.writeValueAsString(rsp));

                        out.flush();

                        out.close();

                    }

                })

                .failureHandler(new AuthenticationFailureHandler() {

                    @Override

                    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {

                        httpServletResponse.setContentType("application/json;charset=utf-8");

                        ObjectMapper objectMapper = new ObjectMapper();

                        ResponseMsg rsp = new ResponseMsg(500,"fail",e.getMessage());

                        PrintWriter out = httpServletResponse.getWriter();

                        out.write(objectMapper.writeValueAsString(rsp));

                        out.flush();

                        out.close();

                    }

                })

                .permitAll()

                .and()

                .sessionManagement()

                //.invalidSessionUrl("/session/invalid")//session过期后跳转的url

                .invalidSessionStrategy(new InvalidSessionStrategy() {

                    @Override

                    public void onInvalidSessionDetected(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException, ServletException {

                        ObjectMapper objectMapper = new ObjectMapper();

                        Result result = new Result(CodeUtil.INVALID_SESSION.getCode(),CodeUtil.INVALID_SESSION.getMessage(),"invalid");

                        PrintWriter out = httpServletResponse.getWriter();

                        out.write(objectMapper.writeValueAsString(result));

                        out.flush();

                        out.close();

                    }

                })

                .and()

                .logout()

                .logoutUrl("/logout")

                .clearAuthentication(true)

                .invalidateHttpSession(true)

                .addLogoutHandler(new LogoutHandler() {

                    @Override

                    public void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) {

                        //此处可做一些清除工作

                    }

                })

                .logoutSuccessHandler(new LogoutSuccessHandler() {

                    @Override

                    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {

                        ResponseMsg rsp = new ResponseMsg(200,"ok","登出成功");

                        PrintWriter out = httpServletResponse.getWriter();

                        ObjectMapper objectMapper = new ObjectMapper();

                        out.write(objectMapper.writeValueAsString(rsp));

                        out.flush();

                        out.close();

                    }

                })

                .permitAll()

                .and()

                .exceptionHandling()

                .accessDeniedHandler(new AccessDeniedHandler() {

                    @Override

                    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {

                        PrintWriter out = httpServletResponse.getWriter();

                        ResponseMsg rsp = new ResponseMsg(401,"fail","权限不足");

                        ObjectMapper objectMapper = new ObjectMapper();

                        out.write(objectMapper.writeValueAsString(rsp));

                        out.flush();

                        out.close();

                    }

                })

                .authenticationEntryPoint(new AuthenticationEntryPoint() {

                    //前后端分离前端不会302重定向,所以要重写AuthenticationEntryPoint

 

                    @Override

                    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {

                        ObjectMapper objectMapper = new ObjectMapper();

                        Result result = new Result(CodeUtil.NOT_LOGIN.getCode(),CodeUtil.NOT_LOGIN.getMessage(),"fail");

                        PrintWriter out = httpServletResponse.getWriter();

                        out.write(objectMapper.writeValueAsString(result));

                        out.flush();

                        out.close();

                    }

                })

                .and()

                .csrf()

                .disable()

                .cors();

    }

}

这里因为前后端分离,所以需要successHandler和failureHandler对用户登录成功和失败进行定制返回。


而如果权限不足需要accessDeniedHandler处理权限不足时的返回。logoutSucessHandler同理。


loginPage()方法是指定用户如果未登录,跳转到那个url上,我们就在Controller上映射上对应到处理即可(这里注意如果用户未登录,那么访问任何url都是跳转到loginPage()指定的url上,只有登录后才会有权限不足的跳转)


loginProcessingUrl()则是指定用户登录提交的的地址,usernameParameter()和passwordParameter()是指定用户登录表单中用户名和密码的name属性值(前端内容)...


permitAll()是指前一个antMatcher的地址未登录时可以访问,不受保护,antMatcher().hasRole(/**/)或者antMatcher().access(/**/)就是表示地址受保护,只有满足权限才能访问。不够权限就会用accessDeniedHandler()中的内容处理。


可能会有人问,那么Security怎么验证用户呢,一个配置类继承WebSecurityConfigurerAdapter类后重写方法:


@Autowired

private UserService userService;

 

@Bean

public PasswordEncoder getPasswordEncoder(){

    //前后端分离要注入一个PasswordEncoder来给successHandler使用

    return new BCryptPasswordEncoder();

}

@Override

protected void configure(AuthenticationManagerBuilder auth) throws Exception {

    super.configure(auth);

    auth.userDetailsService(userService);

}

 


UserService:


package com.jlau.schoollocationsystem.service;

 

import com.fasterxml.jackson.databind.JsonNode;

import com.fasterxml.jackson.databind.ObjectMapper;

import com.jlau.schoollocationsystem.model.OrdinaryUser;

import com.jlau.schoollocationsystem.model.Role;

import com.jlau.schoollocationsystem.model.WXUser;

import com.jlau.schoollocationsystem.repository.RoleRepsitory;

import com.jlau.schoollocationsystem.repository.UserRepository;

import com.jlau.schoollocationsystem.utils.HttpClient;

import com.jlau.schoollocationsystem.utils.ResponseMsg;

import com.jlau.schoollocationsystem.utils.WXUtils;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Qualifier;

import org.springframework.beans.factory.annotation.Value;

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 org.springframework.util.DigestUtils;

 

import java.text.SimpleDateFormat;

import java.util.ArrayList;

import java.util.Date;

import java.util.List;

 

/**

 * Created by cxr1205628673 on 2020/3/10.

 */

@Service

public class UserService implements UserDetailsService{

 

    @Qualifier(value = "wxUserRepository")

    @Autowired

    private UserRepository<WXUser> userRepository;

    @Qualifier(value = "ordinaryUserRepository")

    @Autowired

    private UserRepository<OrdinaryUser> ordinaryUserUserRepository;

    @Autowired

    private RoleRepsitory roleRepsitory;

    @Autowired

    private PasswordEncoder coding;

    @Autowired

    ObjectMapper objectMapper;

    @Value("${wx.appid}")

    String appId;

    @Value("${wx.secret}")

    String secert;

 

    public ResponseMsg userWXLogin(String code,String encryptedData,String iv) throws Exception,NullPointerException {

        final String accessUrl = "https://api.weixin.qq.com/sns/jscode2session?" +

                "appid=" + appId + "&secret=" + secert + "&js_code=" + code + "&grant_type=authorization_code";

        String result = HttpClient.sendGet(accessUrl);

        JsonNode authResult = null;

        JsonNode userInfoResult = null;

        authResult = objectMapper.readTree(result);

        if(authResult.has("errcode")) {

            throw new Exception("授权失败" + authResult.get("errmsg").asText());

        }

        String openId = authResult.get("openid").asText();

        String sessionKey = authResult.get("session_key").asText();

        JsonNode userInfo = WXUtils.getUserInfo(encryptedData,sessionKey,iv);

        String nickName = userInfo.get("nickName").asText();

        String gender = userInfo.get("gender").asText();

        String province = userInfo.get("province").asText();

        String avatarUrl = userInfo.get("avatarUrl").asText();

        String city = userInfo.get("city").asText();

        WXUser user = new WXUser();

        user.setAvatarUrl(avatarUrl);

        user.setGender(Integer.parseInt(gender));

        user.setLanguage(province + city);

        user.setNickName(nickName);

        user.setOpenId(openId);

        user.setLoginTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));

        userRepository.addUser(user);

        return new ResponseMsg(200, "ok", result);

    }

    public ResponseMsg ordinaryUserLogin(String username,String password) throws Exception{

        String encrypPassword = coding.encode(password);

        OrdinaryUser ouser = ordinaryUserUserRepository.findOneUser(username);

        if(ouser != null && ouser.getUsername().equals(username) && ouser.getPassword().equals(encrypPassword)) {

            return new ResponseMsg(200,"ok","登录成功");

        }else {

            throw new Exception("用户名或密码不正确");

        }

    }

    public ResponseMsg ordinaryUserRegister(String username,String password) throws Exception{

        String encrypPassword = coding.encode(password);

        //DigestUtils.md5DigestAsHex(password.getBytes());

        OrdinaryUser ouser = ordinaryUserUserRepository.findOneUser(username);

        if(ouser != null){

            throw new Exception("用户已经存在");

        }

        OrdinaryUser user = new OrdinaryUser();

        user.setUsername(username);

        user.setPassword(encrypPassword);

        List roles = new ArrayList<>();

        Role role = roleRepsitory.findRole("ROLE_USER");

        roles.add(role);

        user.setRoles(roles);

        ordinaryUserUserRepository.addUser(user);

        return new ResponseMsg(200,"ok","注册成功");

    }

 

    public ResponseMsg deleteOrdinaryUser(String id) {

        ordinaryUserUserRepository.deleteUser(id);

        return new ResponseMsg(200,"ok","删除成功");

    }

    public ResponseMsg updateOrdinaryUser(OrdinaryUser user) {

        ordinaryUserUserRepository.updateUser(user);

        return new ResponseMsg(200,"ok","修改成功");

    }

    public ResponseMsg findUser(String id) {

        ordinaryUserUserRepository.findOneUser(id);

        return new ResponseMsg(200,"ok","ok");

    }

    @Override

    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        OrdinaryUser user = ordinaryUserUserRepository.findOneUser(username);

        if(user == null) {

            throw new UsernameNotFoundException("该账号不存在");

        }

        return user;

    }

}

————————————————

版权声明:本文为CSDN博主「飞翔的小菜鸟0x05ac6」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/qq_41910048/article/details/104997710


相关标签: # java

文字广告3/月 文字广告3/月 文字广告3/月 文字广告3/月 文字广告3/月
文字广告3/月 文字广告3/月 文字广告3/月 文字广告3/月 文字广告3/月
文字广告3/月 文字广告3/月 文字广告3/月 文字广告3/月 文字广告3/月
文字广告3/月 文字广告3/月 文字广告3/月 文字广告3/月 文字广告3/月
文字广告3/月 文字广告3/月 文字广告3/月 文字广告3/月 文字广告3/月
 暂无评论,快来抢沙发吧~

发布评论